diff --git a/.hgtags-top-repo b/.hgtags-top-repo
index ae78a92b7f7..9de50aa4824 100644
--- a/.hgtags-top-repo
+++ b/.hgtags-top-repo
@@ -345,3 +345,4 @@ cf1dc4c035fb84693d4ae5ad818785cb4d1465d1 jdk9-b90
 c1f30ac14db0eaff398429c04cd9fab92e1b4b2a jdk-9+100
 c4d72a1620835b5d657b7b6792c2879367d0154f jdk-9+101
 6406ecf5d39482623225bb1b3098c2cac6f7d450 jdk-9+102
+47d6462e514b2097663305a57d9c844c15d5b609 jdk-9+103
diff --git a/common/autoconf/generated-configure.sh b/common/autoconf/generated-configure.sh
index c295b3bf838..0fdc4605014 100644
--- a/common/autoconf/generated-configure.sh
+++ b/common/autoconf/generated-configure.sh
@@ -787,8 +787,6 @@ CPP
 ac_ct_CXX
 CXXFLAGS
 CXX
-ac_ct_PROPER_COMPILER_CXX
-PROPER_COMPILER_CXX
 TOOLCHAIN_PATH_CXX
 POTENTIAL_CXX
 OBJEXT
@@ -798,8 +796,6 @@ CPPFLAGS
 LDFLAGS
 CFLAGS
 CC
-ac_ct_PROPER_COMPILER_CC
-PROPER_COMPILER_CC
 TOOLCHAIN_PATH_CC
 POTENTIAL_CC
 TOOLCHAIN_VERSION
@@ -4119,16 +4115,6 @@ pkgadd_help() {
 
 
 
-    # -g is already added by ENABLE_DEBUG_SYMBOLS and the hotspot makefiles
-    # will basically do slowdebug builds when DEBUG_BINARIES is set for
-    # fastdebug builds
-    DEBUG_BINARIES=false
-    # Fastdebug builds with this setting will essentially be slowdebug
-    # in hotspot.
-    # -g is already added by ENABLE_DEBUG_SYMBOLS and the hotspot makefiles
-    # will basically do slowdebug builds when DEBUG_BINARIES is set for
-    # fastdebug builds
-    DEBUG_BINARIES=false
 #
 # Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -4849,7 +4835,7 @@ VS_SDK_PLATFORM_NAME_2013=
 #CUSTOM_AUTOCONF_INCLUDE
 
 # Do not change or remove the following line, it is needed for consistency checks:
-DATE_WHEN_GENERATED=1453385294
+DATE_WHEN_GENERATED=1454146111
 
 ###############################################################################
 #
@@ -32022,12 +32008,10 @@ $as_echo "$as_me: Rewriting CC to \"$new_complete\"" >&6;}
   fi
 
   TEST_COMPILER="$CC"
-  # Don't remove symbolic links on AIX because 'xlc_r' and 'xlC_r' may all be links
-  # to 'xlc' but it is crucial that we invoke the compiler with the right name!
-  if test "x$OPENJDK_BUILD_OS" != xaix; then
-    # FIXME: This test should not be needed anymore; we don't do that for any platform.
-    { $as_echo "$as_me:${as_lineno-$LINENO}: checking resolved symbolic links for CC" >&5
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking resolved symbolic links for CC" >&5
 $as_echo_n "checking resolved symbolic links for CC... " >&6; }
+  SYMLINK_ORIGINAL="$TEST_COMPILER"
 
   if test "x$OPENJDK_BUILD_OS" != xwindows; then
     # Follow a chain of symbolic links. Use readlink
@@ -32046,13 +32030,13 @@ $as_echo_n "checking resolved symbolic links for CC... " >&6; }
     fi
 
     if test "x$READLINK" != x; then
-      TEST_COMPILER=`$READLINK -f $TEST_COMPILER`
+      SYMLINK_ORIGINAL=`$READLINK -f $SYMLINK_ORIGINAL`
     else
       # Save the current directory for restoring afterwards
       STARTDIR=$PWD
       COUNTER=0
-      sym_link_dir=`$DIRNAME $TEST_COMPILER`
-      sym_link_file=`$BASENAME $TEST_COMPILER`
+      sym_link_dir=`$DIRNAME $SYMLINK_ORIGINAL`
+      sym_link_file=`$BASENAME $SYMLINK_ORIGINAL`
       cd $sym_link_dir
       # Use -P flag to resolve symlinks in directories.
       cd `$THEPWDCMD -P`
@@ -32072,474 +32056,25 @@ $as_echo_n "checking resolved symbolic links for CC... " >&6; }
         let COUNTER=COUNTER+1
       done
       cd $STARTDIR
-      TEST_COMPILER=$sym_link_dir/$sym_link_file
+      SYMLINK_ORIGINAL=$sym_link_dir/$sym_link_file
     fi
   fi
 
-    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $TEST_COMPILER" >&5
-$as_echo "$TEST_COMPILER" >&6; }
-  fi
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking if CC is disguised ccache" >&5
-$as_echo_n "checking if CC is disguised ccache... " >&6; }
-
-  COMPILER_BASENAME=`$BASENAME "$TEST_COMPILER"`
-  if test "x$COMPILER_BASENAME" = "xccache"; then
-    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes, trying to find proper $COMPILER_NAME compiler" >&5
-$as_echo "yes, trying to find proper $COMPILER_NAME compiler" >&6; }
-    # We /usr/lib/ccache in the path, so cc is a symlink to /usr/bin/ccache.
-    # We want to control ccache invocation ourselves, so ignore this cc and try
-    # searching again.
-
-    # Remove the path to the fake ccache cc from the PATH
-    RETRY_COMPILER_SAVED_PATH="$PATH"
-    COMPILER_DIRNAME=`$DIRNAME $CC`
-    PATH="`$ECHO $PATH | $SED -e "s,$COMPILER_DIRNAME,,g" -e "s,::,:,g" -e "s,^:,,g"`"
-
-    # Try again looking for our compiler
-    if test -n "$ac_tool_prefix"; then
-  for ac_prog in $TOOLCHAIN_CC_BINARY
-  do
-    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
-set dummy $ac_tool_prefix$ac_prog; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_PROPER_COMPILER_CC+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  if test -n "$PROPER_COMPILER_CC"; then
-  ac_cv_prog_PROPER_COMPILER_CC="$PROPER_COMPILER_CC" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-    for ac_exec_ext in '' $ac_executable_extensions; do
-  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
-    ac_cv_prog_PROPER_COMPILER_CC="$ac_tool_prefix$ac_prog"
-    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
-  done
-IFS=$as_save_IFS
-
-fi
-fi
-PROPER_COMPILER_CC=$ac_cv_prog_PROPER_COMPILER_CC
-if test -n "$PROPER_COMPILER_CC"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PROPER_COMPILER_CC" >&5
-$as_echo "$PROPER_COMPILER_CC" >&6; }
-else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-fi
-
-
-    test -n "$PROPER_COMPILER_CC" && break
-  done
-fi
-if test -z "$PROPER_COMPILER_CC"; then
-  ac_ct_PROPER_COMPILER_CC=$PROPER_COMPILER_CC
-  for ac_prog in $TOOLCHAIN_CC_BINARY
-do
-  # Extract the first word of "$ac_prog", so it can be a program name with args.
-set dummy $ac_prog; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_ac_ct_PROPER_COMPILER_CC+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  if test -n "$ac_ct_PROPER_COMPILER_CC"; then
-  ac_cv_prog_ac_ct_PROPER_COMPILER_CC="$ac_ct_PROPER_COMPILER_CC" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-    for ac_exec_ext in '' $ac_executable_extensions; do
-  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
-    ac_cv_prog_ac_ct_PROPER_COMPILER_CC="$ac_prog"
-    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
-  done
-IFS=$as_save_IFS
-
-fi
-fi
-ac_ct_PROPER_COMPILER_CC=$ac_cv_prog_ac_ct_PROPER_COMPILER_CC
-if test -n "$ac_ct_PROPER_COMPILER_CC"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_PROPER_COMPILER_CC" >&5
-$as_echo "$ac_ct_PROPER_COMPILER_CC" >&6; }
-else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-fi
-
-
-  test -n "$ac_ct_PROPER_COMPILER_CC" && break
-done
-
-  if test "x$ac_ct_PROPER_COMPILER_CC" = x; then
-    PROPER_COMPILER_CC=""
+  if test "x$TEST_COMPILER" = "x$SYMLINK_ORIGINAL"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no symlink" >&5
+$as_echo "no symlink" >&6; }
   else
-    case $cross_compiling:$ac_tool_warned in
-yes:)
-{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
-ac_tool_warned=yes ;;
-esac
-    PROPER_COMPILER_CC=$ac_ct_PROPER_COMPILER_CC
-  fi
-fi
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SYMLINK_ORIGINAL" >&5
+$as_echo "$SYMLINK_ORIGINAL" >&6; }
 
-
-  # Only process if variable expands to non-empty
-
-  if test "x$PROPER_COMPILER_CC" != x; then
-    if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then
-
-  # First separate the path from the arguments. This will split at the first
-  # space.
-  complete="$PROPER_COMPILER_CC"
-  path="${complete%% *}"
-  tmp="$complete EOL"
-  arguments="${tmp#* }"
-
-  # Input might be given as Windows format, start by converting to
-  # unix format.
-  new_path=`$CYGPATH -u "$path"`
-
-  # Now try to locate executable using which
-  new_path=`$WHICH "$new_path" 2> /dev/null`
-  # bat and cmd files are not always considered executable in cygwin causing which
-  # to not find them
-  if test "x$new_path" = x \
-      && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \
-      && test "x`$LS \"$path\" 2>/dev/null`" != x; then
-    new_path=`$CYGPATH -u "$path"`
-  fi
-  if test "x$new_path" = x; then
-    # Oops. Which didn't find the executable.
-    # The splitting of arguments from the executable at a space might have been incorrect,
-    # since paths with space are more likely in Windows. Give it another try with the whole
-    # argument.
-    path="$complete"
-    arguments="EOL"
-    new_path=`$CYGPATH -u "$path"`
-    new_path=`$WHICH "$new_path" 2> /dev/null`
-    # bat and cmd files are not always considered executable in cygwin causing which
-    # to not find them
-    if test "x$new_path" = x \
-        && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \
-        && test "x`$LS \"$path\" 2>/dev/null`" != x; then
-      new_path=`$CYGPATH -u "$path"`
+    # We can't handle ccache by gcc wrappers, since we need to know if we're
+    # using ccache. Instead ccache usage must be controlled by a configure option.
+    COMPILER_BASENAME=`$BASENAME "$SYMLINK_ORIGINAL"`
+    if test "x$COMPILER_BASENAME" = "xccache"; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: Please use --enable-ccache instead of providing a wrapped compiler." >&5
+$as_echo "$as_me: Please use --enable-ccache instead of providing a wrapped compiler." >&6;}
+      as_fn_error $? "$TEST_COMPILER is a symbolic link to ccache. This is not supported." "$LINENO" 5
     fi
-    if test "x$new_path" = x; then
-      # It's still not found. Now this is an unrecoverable error.
-      { $as_echo "$as_me:${as_lineno-$LINENO}: The path of PROPER_COMPILER_CC, which resolves as \"$complete\", is not found." >&5
-$as_echo "$as_me: The path of PROPER_COMPILER_CC, which resolves as \"$complete\", is not found." >&6;}
-      has_space=`$ECHO "$complete" | $GREP " "`
-      if test "x$has_space" != x; then
-        { $as_echo "$as_me:${as_lineno-$LINENO}: You might be mixing spaces in the path and extra arguments, which is not allowed." >&5
-$as_echo "$as_me: You might be mixing spaces in the path and extra arguments, which is not allowed." >&6;}
-      fi
-      as_fn_error $? "Cannot locate the the path of PROPER_COMPILER_CC" "$LINENO" 5
-    fi
-  fi
-
-  # Cygwin tries to hide some aspects of the Windows file system, such that binaries are
-  # named .exe but called without that suffix. Therefore, "foo" and "foo.exe" are considered
-  # the same file, most of the time (as in "test -f"). But not when running cygpath -s, then
-  # "foo.exe" is OK but "foo" is an error.
-  #
-  # This test is therefore slightly more accurate than "test -f" to check for file presence.
-  # It is also a way to make sure we got the proper file name for the real test later on.
-  test_shortpath=`$CYGPATH -s -m "$new_path" 2> /dev/null`
-  if test "x$test_shortpath" = x; then
-    # Short path failed, file does not exist as specified.
-    # Try adding .exe or .cmd
-    if test -f "${new_path}.exe"; then
-      input_to_shortpath="${new_path}.exe"
-    elif test -f "${new_path}.cmd"; then
-      input_to_shortpath="${new_path}.cmd"
-    else
-      { $as_echo "$as_me:${as_lineno-$LINENO}: The path of PROPER_COMPILER_CC, which resolves as \"$new_path\", is invalid." >&5
-$as_echo "$as_me: The path of PROPER_COMPILER_CC, which resolves as \"$new_path\", is invalid." >&6;}
-      { $as_echo "$as_me:${as_lineno-$LINENO}: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" >&5
-$as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" >&6;}
-      as_fn_error $? "Cannot locate the the path of PROPER_COMPILER_CC" "$LINENO" 5
-    fi
-  else
-    input_to_shortpath="$new_path"
-  fi
-
-  # Call helper function which possibly converts this using DOS-style short mode.
-  # If so, the updated path is stored in $new_path.
-  new_path="$input_to_shortpath"
-
-  input_path="$input_to_shortpath"
-  # Check if we need to convert this using DOS-style short mode. If the path
-  # contains just simple characters, use it. Otherwise (spaces, weird characters),
-  # take no chances and rewrite it.
-  # Note: m4 eats our [], so we need to use [ and ] instead.
-  has_forbidden_chars=`$ECHO "$input_path" | $GREP [^-._/a-zA-Z0-9]`
-  if test "x$has_forbidden_chars" != x; then
-    # Now convert it to mixed DOS-style, short mode (no spaces, and / instead of \)
-    shortmode_path=`$CYGPATH -s -m -a "$input_path"`
-    path_after_shortmode=`$CYGPATH -u "$shortmode_path"`
-    if test "x$path_after_shortmode" != "x$input_to_shortpath"; then
-      # Going to short mode and back again did indeed matter. Since short mode is
-      # case insensitive, let's make it lowercase to improve readability.
-      shortmode_path=`$ECHO "$shortmode_path" | $TR 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
-      # Now convert it back to Unix-style (cygpath)
-      input_path=`$CYGPATH -u "$shortmode_path"`
-      new_path="$input_path"
-    fi
-  fi
-
-  test_cygdrive_prefix=`$ECHO $input_path | $GREP ^/cygdrive/`
-  if test "x$test_cygdrive_prefix" = x; then
-    # As a simple fix, exclude /usr/bin since it's not a real path.
-    if test "x`$ECHO $input_to_shortpath | $GREP ^/usr/bin/`" = x; then
-      # The path is in a Cygwin special directory (e.g. /home). We need this converted to
-      # a path prefixed by /cygdrive for fixpath to work.
-      new_path="$CYGWIN_ROOT_PATH$input_path"
-    fi
-  fi
-
-  # remove trailing .exe if any
-  new_path="${new_path/%.exe/}"
-
-    elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then
-
-  # First separate the path from the arguments. This will split at the first
-  # space.
-  complete="$PROPER_COMPILER_CC"
-  path="${complete%% *}"
-  tmp="$complete EOL"
-  arguments="${tmp#* }"
-
-  # Input might be given as Windows format, start by converting to
-  # unix format.
-  new_path="$path"
-
-  windows_path="$new_path"
-  if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then
-    unix_path=`$CYGPATH -u "$windows_path"`
-    new_path="$unix_path"
-  elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then
-    unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'`
-    new_path="$unix_path"
-  fi
-
-
-  # Now try to locate executable using which
-  new_path=`$WHICH "$new_path" 2> /dev/null`
-
-  if test "x$new_path" = x; then
-    # Oops. Which didn't find the executable.
-    # The splitting of arguments from the executable at a space might have been incorrect,
-    # since paths with space are more likely in Windows. Give it another try with the whole
-    # argument.
-    path="$complete"
-    arguments="EOL"
-    new_path="$path"
-
-  windows_path="$new_path"
-  if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then
-    unix_path=`$CYGPATH -u "$windows_path"`
-    new_path="$unix_path"
-  elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then
-    unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'`
-    new_path="$unix_path"
-  fi
-
-
-    new_path=`$WHICH "$new_path" 2> /dev/null`
-    # bat and cmd files are not always considered executable in MSYS causing which
-    # to not find them
-    if test "x$new_path" = x \
-        && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \
-        && test "x`$LS \"$path\" 2>/dev/null`" != x; then
-      new_path="$path"
-
-  windows_path="$new_path"
-  if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then
-    unix_path=`$CYGPATH -u "$windows_path"`
-    new_path="$unix_path"
-  elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then
-    unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'`
-    new_path="$unix_path"
-  fi
-
-    fi
-
-    if test "x$new_path" = x; then
-      # It's still not found. Now this is an unrecoverable error.
-      { $as_echo "$as_me:${as_lineno-$LINENO}: The path of PROPER_COMPILER_CC, which resolves as \"$complete\", is not found." >&5
-$as_echo "$as_me: The path of PROPER_COMPILER_CC, which resolves as \"$complete\", is not found." >&6;}
-      has_space=`$ECHO "$complete" | $GREP " "`
-      if test "x$has_space" != x; then
-        { $as_echo "$as_me:${as_lineno-$LINENO}: You might be mixing spaces in the path and extra arguments, which is not allowed." >&5
-$as_echo "$as_me: You might be mixing spaces in the path and extra arguments, which is not allowed." >&6;}
-      fi
-      as_fn_error $? "Cannot locate the the path of PROPER_COMPILER_CC" "$LINENO" 5
-    fi
-  fi
-
-  # Now new_path has a complete unix path to the binary
-  if test "x`$ECHO $new_path | $GREP ^/bin/`" != x; then
-    # Keep paths in /bin as-is, but remove trailing .exe if any
-    new_path="${new_path/%.exe/}"
-    # Do not save /bin paths to all_fixpath_prefixes!
-  else
-    # Not in mixed or Windows style, start by that.
-    new_path=`cmd //c echo $new_path`
-
-  input_path="$new_path"
-  # Check if we need to convert this using DOS-style short mode. If the path
-  # contains just simple characters, use it. Otherwise (spaces, weird characters),
-  # take no chances and rewrite it.
-  # Note: m4 eats our [], so we need to use [ and ] instead.
-  has_forbidden_chars=`$ECHO "$input_path" | $GREP [^-_/:a-zA-Z0-9]`
-  if test "x$has_forbidden_chars" != x; then
-    # Now convert it to mixed DOS-style, short mode (no spaces, and / instead of \)
-    new_path=`cmd /c "for %A in (\"$input_path\") do @echo %~sA"|$TR \\\\\\\\ / | $TR 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
-  fi
-
-    # Output is in $new_path
-
-  windows_path="$new_path"
-  if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then
-    unix_path=`$CYGPATH -u "$windows_path"`
-    new_path="$unix_path"
-  elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then
-    unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'`
-    new_path="$unix_path"
-  fi
-
-    # remove trailing .exe if any
-    new_path="${new_path/%.exe/}"
-
-    # Save the first 10 bytes of this path to the storage, so fixpath can work.
-    all_fixpath_prefixes=("${all_fixpath_prefixes[@]}" "${new_path:0:10}")
-  fi
-
-    else
-      # We're on a unix platform. Hooray! :)
-      # First separate the path from the arguments. This will split at the first
-      # space.
-      complete="$PROPER_COMPILER_CC"
-      path="${complete%% *}"
-      tmp="$complete EOL"
-      arguments="${tmp#* }"
-
-      # Cannot rely on the command "which" here since it doesn't always work.
-      is_absolute_path=`$ECHO "$path" | $GREP ^/`
-      if test -z "$is_absolute_path"; then
-        # Path to executable is not absolute. Find it.
-        IFS_save="$IFS"
-        IFS=:
-        for p in $PATH; do
-          if test -f "$p/$path" && test -x "$p/$path"; then
-            new_path="$p/$path"
-            break
-          fi
-        done
-        IFS="$IFS_save"
-      else
-        # This is an absolute path, we can use it without further modifications.
-        new_path="$path"
-      fi
-
-      if test "x$new_path" = x; then
-        { $as_echo "$as_me:${as_lineno-$LINENO}: The path of PROPER_COMPILER_CC, which resolves as \"$complete\", is not found." >&5
-$as_echo "$as_me: The path of PROPER_COMPILER_CC, which resolves as \"$complete\", is not found." >&6;}
-        has_space=`$ECHO "$complete" | $GREP " "`
-        if test "x$has_space" != x; then
-          { $as_echo "$as_me:${as_lineno-$LINENO}: This might be caused by spaces in the path, which is not allowed." >&5
-$as_echo "$as_me: This might be caused by spaces in the path, which is not allowed." >&6;}
-        fi
-        as_fn_error $? "Cannot locate the the path of PROPER_COMPILER_CC" "$LINENO" 5
-      fi
-    fi
-
-    # Now join together the path and the arguments once again
-    if test "x$arguments" != xEOL; then
-      new_complete="$new_path ${arguments% *}"
-    else
-      new_complete="$new_path"
-    fi
-
-    if test "x$complete" != "x$new_complete"; then
-      PROPER_COMPILER_CC="$new_complete"
-      { $as_echo "$as_me:${as_lineno-$LINENO}: Rewriting PROPER_COMPILER_CC to \"$new_complete\"" >&5
-$as_echo "$as_me: Rewriting PROPER_COMPILER_CC to \"$new_complete\"" >&6;}
-    fi
-  fi
-
-    PATH="$RETRY_COMPILER_SAVED_PATH"
-
-    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for resolved symbolic links for CC" >&5
-$as_echo_n "checking for resolved symbolic links for CC... " >&6; }
-
-  if test "x$OPENJDK_BUILD_OS" != xwindows; then
-    # Follow a chain of symbolic links. Use readlink
-    # where it exists, else fall back to horribly
-    # complicated shell code.
-    if test "x$READLINK_TESTED" != yes; then
-      # On MacOSX there is a readlink tool with a different
-      # purpose than the GNU readlink tool. Check the found readlink.
-      ISGNU=`$READLINK --version 2>&1 | $GREP GNU`
-      if test "x$ISGNU" = x; then
-        # A readlink that we do not know how to use.
-        # Are there other non-GNU readlinks out there?
-        READLINK_TESTED=yes
-        READLINK=
-      fi
-    fi
-
-    if test "x$READLINK" != x; then
-      PROPER_COMPILER_CC=`$READLINK -f $PROPER_COMPILER_CC`
-    else
-      # Save the current directory for restoring afterwards
-      STARTDIR=$PWD
-      COUNTER=0
-      sym_link_dir=`$DIRNAME $PROPER_COMPILER_CC`
-      sym_link_file=`$BASENAME $PROPER_COMPILER_CC`
-      cd $sym_link_dir
-      # Use -P flag to resolve symlinks in directories.
-      cd `$THEPWDCMD -P`
-      sym_link_dir=`$THEPWDCMD -P`
-      # Resolve file symlinks
-      while test $COUNTER -lt 20; do
-        ISLINK=`$LS -l $sym_link_dir/$sym_link_file | $GREP '\->' | $SED -e 's/.*-> \(.*\)/\1/'`
-        if test "x$ISLINK" == x; then
-          # This is not a symbolic link! We are done!
-          break
-        fi
-        # Again resolve directory symlinks since the target of the just found
-        # link could be in a different directory
-        cd `$DIRNAME $ISLINK`
-        sym_link_dir=`$THEPWDCMD -P`
-        sym_link_file=`$BASENAME $ISLINK`
-        let COUNTER=COUNTER+1
-      done
-      cd $STARTDIR
-      PROPER_COMPILER_CC=$sym_link_dir/$sym_link_file
-    fi
-  fi
-
-    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PROPER_COMPILER_CC" >&5
-$as_echo "$PROPER_COMPILER_CC" >&6; }
-    CC="$PROPER_COMPILER_CC"
-  else
-    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, keeping CC" >&5
-$as_echo "no, keeping CC" >&6; }
   fi
 
 
@@ -33770,12 +33305,10 @@ $as_echo "$as_me: Rewriting CXX to \"$new_complete\"" >&6;}
   fi
 
   TEST_COMPILER="$CXX"
-  # Don't remove symbolic links on AIX because 'xlc_r' and 'xlC_r' may all be links
-  # to 'xlc' but it is crucial that we invoke the compiler with the right name!
-  if test "x$OPENJDK_BUILD_OS" != xaix; then
-    # FIXME: This test should not be needed anymore; we don't do that for any platform.
-    { $as_echo "$as_me:${as_lineno-$LINENO}: checking resolved symbolic links for CXX" >&5
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking resolved symbolic links for CXX" >&5
 $as_echo_n "checking resolved symbolic links for CXX... " >&6; }
+  SYMLINK_ORIGINAL="$TEST_COMPILER"
 
   if test "x$OPENJDK_BUILD_OS" != xwindows; then
     # Follow a chain of symbolic links. Use readlink
@@ -33794,13 +33327,13 @@ $as_echo_n "checking resolved symbolic links for CXX... " >&6; }
     fi
 
     if test "x$READLINK" != x; then
-      TEST_COMPILER=`$READLINK -f $TEST_COMPILER`
+      SYMLINK_ORIGINAL=`$READLINK -f $SYMLINK_ORIGINAL`
     else
       # Save the current directory for restoring afterwards
       STARTDIR=$PWD
       COUNTER=0
-      sym_link_dir=`$DIRNAME $TEST_COMPILER`
-      sym_link_file=`$BASENAME $TEST_COMPILER`
+      sym_link_dir=`$DIRNAME $SYMLINK_ORIGINAL`
+      sym_link_file=`$BASENAME $SYMLINK_ORIGINAL`
       cd $sym_link_dir
       # Use -P flag to resolve symlinks in directories.
       cd `$THEPWDCMD -P`
@@ -33820,474 +33353,25 @@ $as_echo_n "checking resolved symbolic links for CXX... " >&6; }
         let COUNTER=COUNTER+1
       done
       cd $STARTDIR
-      TEST_COMPILER=$sym_link_dir/$sym_link_file
+      SYMLINK_ORIGINAL=$sym_link_dir/$sym_link_file
     fi
   fi
 
-    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $TEST_COMPILER" >&5
-$as_echo "$TEST_COMPILER" >&6; }
-  fi
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking if CXX is disguised ccache" >&5
-$as_echo_n "checking if CXX is disguised ccache... " >&6; }
-
-  COMPILER_BASENAME=`$BASENAME "$TEST_COMPILER"`
-  if test "x$COMPILER_BASENAME" = "xccache"; then
-    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes, trying to find proper $COMPILER_NAME compiler" >&5
-$as_echo "yes, trying to find proper $COMPILER_NAME compiler" >&6; }
-    # We /usr/lib/ccache in the path, so cc is a symlink to /usr/bin/ccache.
-    # We want to control ccache invocation ourselves, so ignore this cc and try
-    # searching again.
-
-    # Remove the path to the fake ccache cc from the PATH
-    RETRY_COMPILER_SAVED_PATH="$PATH"
-    COMPILER_DIRNAME=`$DIRNAME $CXX`
-    PATH="`$ECHO $PATH | $SED -e "s,$COMPILER_DIRNAME,,g" -e "s,::,:,g" -e "s,^:,,g"`"
-
-    # Try again looking for our compiler
-    if test -n "$ac_tool_prefix"; then
-  for ac_prog in $TOOLCHAIN_CXX_BINARY
-  do
-    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
-set dummy $ac_tool_prefix$ac_prog; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_PROPER_COMPILER_CXX+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  if test -n "$PROPER_COMPILER_CXX"; then
-  ac_cv_prog_PROPER_COMPILER_CXX="$PROPER_COMPILER_CXX" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-    for ac_exec_ext in '' $ac_executable_extensions; do
-  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
-    ac_cv_prog_PROPER_COMPILER_CXX="$ac_tool_prefix$ac_prog"
-    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
-  done
-IFS=$as_save_IFS
-
-fi
-fi
-PROPER_COMPILER_CXX=$ac_cv_prog_PROPER_COMPILER_CXX
-if test -n "$PROPER_COMPILER_CXX"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PROPER_COMPILER_CXX" >&5
-$as_echo "$PROPER_COMPILER_CXX" >&6; }
-else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-fi
-
-
-    test -n "$PROPER_COMPILER_CXX" && break
-  done
-fi
-if test -z "$PROPER_COMPILER_CXX"; then
-  ac_ct_PROPER_COMPILER_CXX=$PROPER_COMPILER_CXX
-  for ac_prog in $TOOLCHAIN_CXX_BINARY
-do
-  # Extract the first word of "$ac_prog", so it can be a program name with args.
-set dummy $ac_prog; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_ac_ct_PROPER_COMPILER_CXX+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  if test -n "$ac_ct_PROPER_COMPILER_CXX"; then
-  ac_cv_prog_ac_ct_PROPER_COMPILER_CXX="$ac_ct_PROPER_COMPILER_CXX" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-    for ac_exec_ext in '' $ac_executable_extensions; do
-  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
-    ac_cv_prog_ac_ct_PROPER_COMPILER_CXX="$ac_prog"
-    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
-  done
-IFS=$as_save_IFS
-
-fi
-fi
-ac_ct_PROPER_COMPILER_CXX=$ac_cv_prog_ac_ct_PROPER_COMPILER_CXX
-if test -n "$ac_ct_PROPER_COMPILER_CXX"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_PROPER_COMPILER_CXX" >&5
-$as_echo "$ac_ct_PROPER_COMPILER_CXX" >&6; }
-else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-fi
-
-
-  test -n "$ac_ct_PROPER_COMPILER_CXX" && break
-done
-
-  if test "x$ac_ct_PROPER_COMPILER_CXX" = x; then
-    PROPER_COMPILER_CXX=""
+  if test "x$TEST_COMPILER" = "x$SYMLINK_ORIGINAL"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no symlink" >&5
+$as_echo "no symlink" >&6; }
   else
-    case $cross_compiling:$ac_tool_warned in
-yes:)
-{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
-ac_tool_warned=yes ;;
-esac
-    PROPER_COMPILER_CXX=$ac_ct_PROPER_COMPILER_CXX
-  fi
-fi
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SYMLINK_ORIGINAL" >&5
+$as_echo "$SYMLINK_ORIGINAL" >&6; }
 
-
-  # Only process if variable expands to non-empty
-
-  if test "x$PROPER_COMPILER_CXX" != x; then
-    if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then
-
-  # First separate the path from the arguments. This will split at the first
-  # space.
-  complete="$PROPER_COMPILER_CXX"
-  path="${complete%% *}"
-  tmp="$complete EOL"
-  arguments="${tmp#* }"
-
-  # Input might be given as Windows format, start by converting to
-  # unix format.
-  new_path=`$CYGPATH -u "$path"`
-
-  # Now try to locate executable using which
-  new_path=`$WHICH "$new_path" 2> /dev/null`
-  # bat and cmd files are not always considered executable in cygwin causing which
-  # to not find them
-  if test "x$new_path" = x \
-      && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \
-      && test "x`$LS \"$path\" 2>/dev/null`" != x; then
-    new_path=`$CYGPATH -u "$path"`
-  fi
-  if test "x$new_path" = x; then
-    # Oops. Which didn't find the executable.
-    # The splitting of arguments from the executable at a space might have been incorrect,
-    # since paths with space are more likely in Windows. Give it another try with the whole
-    # argument.
-    path="$complete"
-    arguments="EOL"
-    new_path=`$CYGPATH -u "$path"`
-    new_path=`$WHICH "$new_path" 2> /dev/null`
-    # bat and cmd files are not always considered executable in cygwin causing which
-    # to not find them
-    if test "x$new_path" = x \
-        && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \
-        && test "x`$LS \"$path\" 2>/dev/null`" != x; then
-      new_path=`$CYGPATH -u "$path"`
+    # We can't handle ccache by gcc wrappers, since we need to know if we're
+    # using ccache. Instead ccache usage must be controlled by a configure option.
+    COMPILER_BASENAME=`$BASENAME "$SYMLINK_ORIGINAL"`
+    if test "x$COMPILER_BASENAME" = "xccache"; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: Please use --enable-ccache instead of providing a wrapped compiler." >&5
+$as_echo "$as_me: Please use --enable-ccache instead of providing a wrapped compiler." >&6;}
+      as_fn_error $? "$TEST_COMPILER is a symbolic link to ccache. This is not supported." "$LINENO" 5
     fi
-    if test "x$new_path" = x; then
-      # It's still not found. Now this is an unrecoverable error.
-      { $as_echo "$as_me:${as_lineno-$LINENO}: The path of PROPER_COMPILER_CXX, which resolves as \"$complete\", is not found." >&5
-$as_echo "$as_me: The path of PROPER_COMPILER_CXX, which resolves as \"$complete\", is not found." >&6;}
-      has_space=`$ECHO "$complete" | $GREP " "`
-      if test "x$has_space" != x; then
-        { $as_echo "$as_me:${as_lineno-$LINENO}: You might be mixing spaces in the path and extra arguments, which is not allowed." >&5
-$as_echo "$as_me: You might be mixing spaces in the path and extra arguments, which is not allowed." >&6;}
-      fi
-      as_fn_error $? "Cannot locate the the path of PROPER_COMPILER_CXX" "$LINENO" 5
-    fi
-  fi
-
-  # Cygwin tries to hide some aspects of the Windows file system, such that binaries are
-  # named .exe but called without that suffix. Therefore, "foo" and "foo.exe" are considered
-  # the same file, most of the time (as in "test -f"). But not when running cygpath -s, then
-  # "foo.exe" is OK but "foo" is an error.
-  #
-  # This test is therefore slightly more accurate than "test -f" to check for file presence.
-  # It is also a way to make sure we got the proper file name for the real test later on.
-  test_shortpath=`$CYGPATH -s -m "$new_path" 2> /dev/null`
-  if test "x$test_shortpath" = x; then
-    # Short path failed, file does not exist as specified.
-    # Try adding .exe or .cmd
-    if test -f "${new_path}.exe"; then
-      input_to_shortpath="${new_path}.exe"
-    elif test -f "${new_path}.cmd"; then
-      input_to_shortpath="${new_path}.cmd"
-    else
-      { $as_echo "$as_me:${as_lineno-$LINENO}: The path of PROPER_COMPILER_CXX, which resolves as \"$new_path\", is invalid." >&5
-$as_echo "$as_me: The path of PROPER_COMPILER_CXX, which resolves as \"$new_path\", is invalid." >&6;}
-      { $as_echo "$as_me:${as_lineno-$LINENO}: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" >&5
-$as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" >&6;}
-      as_fn_error $? "Cannot locate the the path of PROPER_COMPILER_CXX" "$LINENO" 5
-    fi
-  else
-    input_to_shortpath="$new_path"
-  fi
-
-  # Call helper function which possibly converts this using DOS-style short mode.
-  # If so, the updated path is stored in $new_path.
-  new_path="$input_to_shortpath"
-
-  input_path="$input_to_shortpath"
-  # Check if we need to convert this using DOS-style short mode. If the path
-  # contains just simple characters, use it. Otherwise (spaces, weird characters),
-  # take no chances and rewrite it.
-  # Note: m4 eats our [], so we need to use [ and ] instead.
-  has_forbidden_chars=`$ECHO "$input_path" | $GREP [^-._/a-zA-Z0-9]`
-  if test "x$has_forbidden_chars" != x; then
-    # Now convert it to mixed DOS-style, short mode (no spaces, and / instead of \)
-    shortmode_path=`$CYGPATH -s -m -a "$input_path"`
-    path_after_shortmode=`$CYGPATH -u "$shortmode_path"`
-    if test "x$path_after_shortmode" != "x$input_to_shortpath"; then
-      # Going to short mode and back again did indeed matter. Since short mode is
-      # case insensitive, let's make it lowercase to improve readability.
-      shortmode_path=`$ECHO "$shortmode_path" | $TR 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
-      # Now convert it back to Unix-style (cygpath)
-      input_path=`$CYGPATH -u "$shortmode_path"`
-      new_path="$input_path"
-    fi
-  fi
-
-  test_cygdrive_prefix=`$ECHO $input_path | $GREP ^/cygdrive/`
-  if test "x$test_cygdrive_prefix" = x; then
-    # As a simple fix, exclude /usr/bin since it's not a real path.
-    if test "x`$ECHO $input_to_shortpath | $GREP ^/usr/bin/`" = x; then
-      # The path is in a Cygwin special directory (e.g. /home). We need this converted to
-      # a path prefixed by /cygdrive for fixpath to work.
-      new_path="$CYGWIN_ROOT_PATH$input_path"
-    fi
-  fi
-
-  # remove trailing .exe if any
-  new_path="${new_path/%.exe/}"
-
-    elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then
-
-  # First separate the path from the arguments. This will split at the first
-  # space.
-  complete="$PROPER_COMPILER_CXX"
-  path="${complete%% *}"
-  tmp="$complete EOL"
-  arguments="${tmp#* }"
-
-  # Input might be given as Windows format, start by converting to
-  # unix format.
-  new_path="$path"
-
-  windows_path="$new_path"
-  if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then
-    unix_path=`$CYGPATH -u "$windows_path"`
-    new_path="$unix_path"
-  elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then
-    unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'`
-    new_path="$unix_path"
-  fi
-
-
-  # Now try to locate executable using which
-  new_path=`$WHICH "$new_path" 2> /dev/null`
-
-  if test "x$new_path" = x; then
-    # Oops. Which didn't find the executable.
-    # The splitting of arguments from the executable at a space might have been incorrect,
-    # since paths with space are more likely in Windows. Give it another try with the whole
-    # argument.
-    path="$complete"
-    arguments="EOL"
-    new_path="$path"
-
-  windows_path="$new_path"
-  if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then
-    unix_path=`$CYGPATH -u "$windows_path"`
-    new_path="$unix_path"
-  elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then
-    unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'`
-    new_path="$unix_path"
-  fi
-
-
-    new_path=`$WHICH "$new_path" 2> /dev/null`
-    # bat and cmd files are not always considered executable in MSYS causing which
-    # to not find them
-    if test "x$new_path" = x \
-        && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \
-        && test "x`$LS \"$path\" 2>/dev/null`" != x; then
-      new_path="$path"
-
-  windows_path="$new_path"
-  if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then
-    unix_path=`$CYGPATH -u "$windows_path"`
-    new_path="$unix_path"
-  elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then
-    unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'`
-    new_path="$unix_path"
-  fi
-
-    fi
-
-    if test "x$new_path" = x; then
-      # It's still not found. Now this is an unrecoverable error.
-      { $as_echo "$as_me:${as_lineno-$LINENO}: The path of PROPER_COMPILER_CXX, which resolves as \"$complete\", is not found." >&5
-$as_echo "$as_me: The path of PROPER_COMPILER_CXX, which resolves as \"$complete\", is not found." >&6;}
-      has_space=`$ECHO "$complete" | $GREP " "`
-      if test "x$has_space" != x; then
-        { $as_echo "$as_me:${as_lineno-$LINENO}: You might be mixing spaces in the path and extra arguments, which is not allowed." >&5
-$as_echo "$as_me: You might be mixing spaces in the path and extra arguments, which is not allowed." >&6;}
-      fi
-      as_fn_error $? "Cannot locate the the path of PROPER_COMPILER_CXX" "$LINENO" 5
-    fi
-  fi
-
-  # Now new_path has a complete unix path to the binary
-  if test "x`$ECHO $new_path | $GREP ^/bin/`" != x; then
-    # Keep paths in /bin as-is, but remove trailing .exe if any
-    new_path="${new_path/%.exe/}"
-    # Do not save /bin paths to all_fixpath_prefixes!
-  else
-    # Not in mixed or Windows style, start by that.
-    new_path=`cmd //c echo $new_path`
-
-  input_path="$new_path"
-  # Check if we need to convert this using DOS-style short mode. If the path
-  # contains just simple characters, use it. Otherwise (spaces, weird characters),
-  # take no chances and rewrite it.
-  # Note: m4 eats our [], so we need to use [ and ] instead.
-  has_forbidden_chars=`$ECHO "$input_path" | $GREP [^-_/:a-zA-Z0-9]`
-  if test "x$has_forbidden_chars" != x; then
-    # Now convert it to mixed DOS-style, short mode (no spaces, and / instead of \)
-    new_path=`cmd /c "for %A in (\"$input_path\") do @echo %~sA"|$TR \\\\\\\\ / | $TR 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
-  fi
-
-    # Output is in $new_path
-
-  windows_path="$new_path"
-  if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then
-    unix_path=`$CYGPATH -u "$windows_path"`
-    new_path="$unix_path"
-  elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then
-    unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'`
-    new_path="$unix_path"
-  fi
-
-    # remove trailing .exe if any
-    new_path="${new_path/%.exe/}"
-
-    # Save the first 10 bytes of this path to the storage, so fixpath can work.
-    all_fixpath_prefixes=("${all_fixpath_prefixes[@]}" "${new_path:0:10}")
-  fi
-
-    else
-      # We're on a unix platform. Hooray! :)
-      # First separate the path from the arguments. This will split at the first
-      # space.
-      complete="$PROPER_COMPILER_CXX"
-      path="${complete%% *}"
-      tmp="$complete EOL"
-      arguments="${tmp#* }"
-
-      # Cannot rely on the command "which" here since it doesn't always work.
-      is_absolute_path=`$ECHO "$path" | $GREP ^/`
-      if test -z "$is_absolute_path"; then
-        # Path to executable is not absolute. Find it.
-        IFS_save="$IFS"
-        IFS=:
-        for p in $PATH; do
-          if test -f "$p/$path" && test -x "$p/$path"; then
-            new_path="$p/$path"
-            break
-          fi
-        done
-        IFS="$IFS_save"
-      else
-        # This is an absolute path, we can use it without further modifications.
-        new_path="$path"
-      fi
-
-      if test "x$new_path" = x; then
-        { $as_echo "$as_me:${as_lineno-$LINENO}: The path of PROPER_COMPILER_CXX, which resolves as \"$complete\", is not found." >&5
-$as_echo "$as_me: The path of PROPER_COMPILER_CXX, which resolves as \"$complete\", is not found." >&6;}
-        has_space=`$ECHO "$complete" | $GREP " "`
-        if test "x$has_space" != x; then
-          { $as_echo "$as_me:${as_lineno-$LINENO}: This might be caused by spaces in the path, which is not allowed." >&5
-$as_echo "$as_me: This might be caused by spaces in the path, which is not allowed." >&6;}
-        fi
-        as_fn_error $? "Cannot locate the the path of PROPER_COMPILER_CXX" "$LINENO" 5
-      fi
-    fi
-
-    # Now join together the path and the arguments once again
-    if test "x$arguments" != xEOL; then
-      new_complete="$new_path ${arguments% *}"
-    else
-      new_complete="$new_path"
-    fi
-
-    if test "x$complete" != "x$new_complete"; then
-      PROPER_COMPILER_CXX="$new_complete"
-      { $as_echo "$as_me:${as_lineno-$LINENO}: Rewriting PROPER_COMPILER_CXX to \"$new_complete\"" >&5
-$as_echo "$as_me: Rewriting PROPER_COMPILER_CXX to \"$new_complete\"" >&6;}
-    fi
-  fi
-
-    PATH="$RETRY_COMPILER_SAVED_PATH"
-
-    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for resolved symbolic links for CXX" >&5
-$as_echo_n "checking for resolved symbolic links for CXX... " >&6; }
-
-  if test "x$OPENJDK_BUILD_OS" != xwindows; then
-    # Follow a chain of symbolic links. Use readlink
-    # where it exists, else fall back to horribly
-    # complicated shell code.
-    if test "x$READLINK_TESTED" != yes; then
-      # On MacOSX there is a readlink tool with a different
-      # purpose than the GNU readlink tool. Check the found readlink.
-      ISGNU=`$READLINK --version 2>&1 | $GREP GNU`
-      if test "x$ISGNU" = x; then
-        # A readlink that we do not know how to use.
-        # Are there other non-GNU readlinks out there?
-        READLINK_TESTED=yes
-        READLINK=
-      fi
-    fi
-
-    if test "x$READLINK" != x; then
-      PROPER_COMPILER_CXX=`$READLINK -f $PROPER_COMPILER_CXX`
-    else
-      # Save the current directory for restoring afterwards
-      STARTDIR=$PWD
-      COUNTER=0
-      sym_link_dir=`$DIRNAME $PROPER_COMPILER_CXX`
-      sym_link_file=`$BASENAME $PROPER_COMPILER_CXX`
-      cd $sym_link_dir
-      # Use -P flag to resolve symlinks in directories.
-      cd `$THEPWDCMD -P`
-      sym_link_dir=`$THEPWDCMD -P`
-      # Resolve file symlinks
-      while test $COUNTER -lt 20; do
-        ISLINK=`$LS -l $sym_link_dir/$sym_link_file | $GREP '\->' | $SED -e 's/.*-> \(.*\)/\1/'`
-        if test "x$ISLINK" == x; then
-          # This is not a symbolic link! We are done!
-          break
-        fi
-        # Again resolve directory symlinks since the target of the just found
-        # link could be in a different directory
-        cd `$DIRNAME $ISLINK`
-        sym_link_dir=`$THEPWDCMD -P`
-        sym_link_file=`$BASENAME $ISLINK`
-        let COUNTER=COUNTER+1
-      done
-      cd $STARTDIR
-      PROPER_COMPILER_CXX=$sym_link_dir/$sym_link_file
-    fi
-  fi
-
-    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PROPER_COMPILER_CXX" >&5
-$as_echo "$PROPER_COMPILER_CXX" >&6; }
-    CXX="$PROPER_COMPILER_CXX"
-  else
-    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, keeping CXX" >&5
-$as_echo "no, keeping CXX" >&6; }
   fi
 
 
@@ -58558,6 +57642,10 @@ fi
       fi
     done
 
+    # Due to https://llvm.org/bugs/show_bug.cgi?id=16902, llvm does not
+    # always properly detect -ltinfo
+    LLVM_LIBS="${LLVM_LIBS} -ltinfo"
+
 
 
 
diff --git a/common/autoconf/hotspot.m4 b/common/autoconf/hotspot.m4
index e6849af3af4..ec357f99572 100644
--- a/common/autoconf/hotspot.m4
+++ b/common/autoconf/hotspot.m4
@@ -266,14 +266,3 @@ AC_DEFUN_ONCE([HOTSPOT_SETUP_BUILD_TWEAKS],
   HOTSPOT_MAKE_ARGS="$HOTSPOT_TARGET"
   AC_SUBST(HOTSPOT_MAKE_ARGS)
 ])
-
-    # -g is already added by ENABLE_DEBUG_SYMBOLS and the hotspot makefiles
-    # will basically do slowdebug builds when DEBUG_BINARIES is set for
-    # fastdebug builds
-    DEBUG_BINARIES=false
-    # Fastdebug builds with this setting will essentially be slowdebug
-    # in hotspot.
-    # -g is already added by ENABLE_DEBUG_SYMBOLS and the hotspot makefiles
-    # will basically do slowdebug builds when DEBUG_BINARIES is set for
-    # fastdebug builds
-    DEBUG_BINARIES=false
\ No newline at end of file
diff --git a/common/autoconf/libraries.m4 b/common/autoconf/libraries.m4
index cb3cddd7f92..7bae405555e 100644
--- a/common/autoconf/libraries.m4
+++ b/common/autoconf/libraries.m4
@@ -144,6 +144,10 @@ AC_DEFUN_ONCE([LIB_SETUP_LLVM],
       fi
     done
 
+    # Due to https://llvm.org/bugs/show_bug.cgi?id=16902, llvm does not
+    # always properly detect -ltinfo
+    LLVM_LIBS="${LLVM_LIBS} -ltinfo"
+
     AC_SUBST(LLVM_CFLAGS)
     AC_SUBST(LLVM_LDFLAGS)
     AC_SUBST(LLVM_LIBS)
diff --git a/common/autoconf/spec.gmk.in b/common/autoconf/spec.gmk.in
index e2a73d20ccf..a78b30ba5ad 100644
--- a/common/autoconf/spec.gmk.in
+++ b/common/autoconf/spec.gmk.in
@@ -500,7 +500,7 @@ JAVAC_FLAGS?=@JAVAC_FLAGS@
 INTERIM_LANGTOOLS_JAR = $(BUILDTOOLS_OUTPUTDIR)/interim_langtools.jar
 INTERIM_LANGTOOLS_ARGS = "-Xbootclasspath/p:$(INTERIM_LANGTOOLS_JAR)" -cp $(INTERIM_LANGTOOLS_JAR)
 NEW_JAVAC   = $(INTERIM_LANGTOOLS_ARGS) com.sun.tools.javac.Main
-NEW_JAVADOC = $(INTERIM_LANGTOOLS_ARGS) com.sun.tools.javadoc.Main
+NEW_JAVADOC = $(INTERIM_LANGTOOLS_ARGS) jdk.javadoc.internal.tool.Main 
 
 # Base flags for RC
 # Guarding this against resetting value. Legacy make files include spec multiple
diff --git a/common/autoconf/toolchain.m4 b/common/autoconf/toolchain.m4
index 4254d86a4fb..5741294f309 100644
--- a/common/autoconf/toolchain.m4
+++ b/common/autoconf/toolchain.m4
@@ -433,39 +433,22 @@ AC_DEFUN([TOOLCHAIN_FIND_COMPILER],
   # Now we have a compiler binary in $1. Make sure it's okay.
   BASIC_FIXUP_EXECUTABLE($1)
   TEST_COMPILER="[$]$1"
-  # Don't remove symbolic links on AIX because 'xlc_r' and 'xlC_r' may all be links
-  # to 'xlc' but it is crucial that we invoke the compiler with the right name!
-  if test "x$OPENJDK_BUILD_OS" != xaix; then
-    # FIXME: This test should not be needed anymore; we don't do that for any platform.
-    AC_MSG_CHECKING([resolved symbolic links for $1])
-    BASIC_REMOVE_SYMBOLIC_LINKS(TEST_COMPILER)
-    AC_MSG_RESULT([$TEST_COMPILER])
-  fi
-  AC_MSG_CHECKING([if $1 is disguised ccache])
 
-  COMPILER_BASENAME=`$BASENAME "$TEST_COMPILER"`
-  if test "x$COMPILER_BASENAME" = "xccache"; then
-    AC_MSG_RESULT([yes, trying to find proper $COMPILER_NAME compiler])
-    # We /usr/lib/ccache in the path, so cc is a symlink to /usr/bin/ccache.
-    # We want to control ccache invocation ourselves, so ignore this cc and try
-    # searching again.
-
-    # Remove the path to the fake ccache cc from the PATH
-    RETRY_COMPILER_SAVED_PATH="$PATH"
-    COMPILER_DIRNAME=`$DIRNAME [$]$1`
-    PATH="`$ECHO $PATH | $SED -e "s,$COMPILER_DIRNAME,,g" -e "s,::,:,g" -e "s,^:,,g"`"
-
-    # Try again looking for our compiler
-    AC_CHECK_TOOLS(PROPER_COMPILER_$1, $3)
-    BASIC_FIXUP_EXECUTABLE(PROPER_COMPILER_$1)
-    PATH="$RETRY_COMPILER_SAVED_PATH"
-
-    AC_MSG_CHECKING([for resolved symbolic links for $1])
-    BASIC_REMOVE_SYMBOLIC_LINKS(PROPER_COMPILER_$1)
-    AC_MSG_RESULT([$PROPER_COMPILER_$1])
-    $1="$PROPER_COMPILER_$1"
+  AC_MSG_CHECKING([resolved symbolic links for $1])
+  SYMLINK_ORIGINAL="$TEST_COMPILER"
+  BASIC_REMOVE_SYMBOLIC_LINKS(SYMLINK_ORIGINAL)
+  if test "x$TEST_COMPILER" = "x$SYMLINK_ORIGINAL"; then
+    AC_MSG_RESULT([no symlink])
   else
-    AC_MSG_RESULT([no, keeping $1])
+    AC_MSG_RESULT([$SYMLINK_ORIGINAL])
+
+    # We can't handle ccache by gcc wrappers, since we need to know if we're
+    # using ccache. Instead ccache usage must be controlled by a configure option.
+    COMPILER_BASENAME=`$BASENAME "$SYMLINK_ORIGINAL"`
+    if test "x$COMPILER_BASENAME" = "xccache"; then
+      AC_MSG_NOTICE([Please use --enable-ccache instead of providing a wrapped compiler.])
+      AC_MSG_ERROR([$TEST_COMPILER is a symbolic link to ccache. This is not supported.])
+    fi
   fi
 
   TOOLCHAIN_CHECK_COMPILER_VERSION([$1], [$COMPILER_NAME])
diff --git a/common/bin/compare.sh b/common/bin/compare.sh
index 9e680f08b0a..b417e8ed819 100644
--- a/common/bin/compare.sh
+++ b/common/bin/compare.sh
@@ -306,7 +306,7 @@ compare_general_files() {
         ! -name "*.lib" ! -name "*.war" ! -name "JavaControlPanel" \
         ! -name "*.obj" ! -name "*.o" ! -name "JavaControlPanelHelper" \
         ! -name "JavaUpdater" ! -name "JavaWSApplicationStub" \
-        ! -name "jspawnhelper" \
+        ! -name "jspawnhelper" ! -name "*.a" \
         | $GREP -v "./bin/"  | $SORT | $FILTER)
 
     echo Other files with binary differences...
@@ -939,7 +939,7 @@ compare_all_libs() {
     WORK_DIR=$3
 
     LIBS=$(cd $THIS_DIR && $FIND . -type f \( -name 'lib*.so' -o -name '*.dylib' \
-        -o -name '*.dll' -o -name '*.obj' -o -name '*.o' \
+        -o -name '*.dll' -o -name '*.obj' -o -name '*.o' -o -name '*.a' \
         -o -name '*.cpl' \) | $SORT | $FILTER)
 
     if [ -n "$LIBS" ]; then
diff --git a/corba/.hgtags b/corba/.hgtags
index d486a0a69bb..ba693c7dd28 100644
--- a/corba/.hgtags
+++ b/corba/.hgtags
@@ -345,3 +345,4 @@ ea285530245cf4e0edf0479121a41347d3030eba jdk-9+98
 791d0d3ac0138faeb6110bd840a4545bc1950df2 jdk-9+100
 30dfb3bd3d06b4bb80a087babc0d1841edba187b jdk-9+101
 9c4662334d933d299928d1f599d02ff50777cbf8 jdk-9+102
+0680fb7dae4da1ee6cf783c4b74184e3e08d3179 jdk-9+103
diff --git a/hotspot/.hgtags b/hotspot/.hgtags
index 4731b6143c4..fbc7ac926fb 100644
--- a/hotspot/.hgtags
+++ b/hotspot/.hgtags
@@ -505,3 +505,4 @@ f008e8cc10d5b3212fb22d58c96fa01d38654f19 jdk-9+99
 bdb0acafc63c42e84d9d8195bf2e2b25ee9c3306 jdk-9+100
 9f45d3d57d6948cf526fbc2e2891a9a74ac6941a jdk-9+101
 d5239fc1b69749ae50793c61b899fcdacf3df857 jdk-9+102
+c5f55130b1b69510d9a6f4a3105b58e21cd7ffe1 jdk-9+103
diff --git a/hotspot/test/gc/metaspace/TestPerfCountersAndMemoryPools.java b/hotspot/test/gc/metaspace/TestPerfCountersAndMemoryPools.java
index c04f8ad9377..5d7d3852f43 100644
--- a/hotspot/test/gc/metaspace/TestPerfCountersAndMemoryPools.java
+++ b/hotspot/test/gc/metaspace/TestPerfCountersAndMemoryPools.java
@@ -64,14 +64,18 @@ public class TestPerfCountersAndMemoryPools {
         throws Exception {
         MemoryPoolMXBean pool = getMemoryPool(memoryPoolName);
 
+        // First, call all the methods to let them allocate their own slab of metadata
+        getMinCapacity(perfNS);
+        getCapacity(perfNS);
+        getUsed(perfNS);
+        pool.getUsage().getInit();
+        pool.getUsage().getUsed();
+        pool.getUsage().getCommitted();
+        assertEQ(1L, 1L);
+
         // Must do a GC to update performance counters
         System.gc();
         assertEQ(getMinCapacity(perfNS), pool.getUsage().getInit());
-
-        // Must do a second GC to update the perfomance counters again, since
-        // the call pool.getUsage().getInit() could have allocated some
-        // metadata.
-        System.gc();
         assertEQ(getUsed(perfNS), pool.getUsage().getUsed());
         assertEQ(getCapacity(perfNS), pool.getUsage().getCommitted());
     }
diff --git a/jaxp/.hgtags b/jaxp/.hgtags
index 06990f8df41..75132348c9c 100644
--- a/jaxp/.hgtags
+++ b/jaxp/.hgtags
@@ -345,3 +345,4 @@ c8d0845877a811ab4350935892f826929359a3ff jdk-9+95
 d45bcd374f6057851e3c2dcd45607cd362afadfa jdk-9+100
 d3e834ff74e724a2b92a558e18e8cbf81c6dbc59 jdk-9+101
 9dcf193c0b6cf22c0e89e2dc705a2c0f520ae064 jdk-9+102
+bdbf2342b21bd8ecad1b4e6499a0dfb314952bd7 jdk-9+103
diff --git a/jaxws/.hgtags b/jaxws/.hgtags
index 25d05bf7a7f..9909ccb59aa 100644
--- a/jaxws/.hgtags
+++ b/jaxws/.hgtags
@@ -348,3 +348,4 @@ b55cebc47555293cf9c2aefb3bf63c56e847ab19 jdk-9+96
 d0a97e57d2336238edf6a4cd60aafe67deb7258d jdk-9+100
 3e99318616da903e0dc8f07f9f9203dc1bd49921 jdk-9+101
 0868b93587cc99df3a4f4d3817a1aa756bea60ab jdk-9+102
+eb5e005a17e50d7d8340daaf21a5c3c5ae358d68 jdk-9+103
diff --git a/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/ContextFinder.java b/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/ContextFinder.java
index a0329cf7744..b6d9f4d2dc9 100644
--- a/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/ContextFinder.java
+++ b/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/ContextFinder.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2016, 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
@@ -29,11 +29,12 @@ import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
-import java.io.UnsupportedEncodingException;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.net.URL;
 import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
 import java.util.Map;
 import java.util.Properties;
 import java.util.StringTokenizer;
@@ -105,9 +106,9 @@ class ContextFinder {
 
     /**
      * If the {@link InvocationTargetException} wraps an exception that shouldn't be wrapped,
-     * throw the wrapped exception.
+     * throw the wrapped exception. Otherwise returns exception to be wrapped for further processing.
      */
-    private static void handleInvocationTargetException(InvocationTargetException x) throws JAXBException {
+    private static Throwable handleInvocationTargetException(InvocationTargetException x) throws JAXBException {
         Throwable t = x.getTargetException();
         if (t != null) {
             if (t instanceof JAXBException)
@@ -118,7 +119,9 @@ class ContextFinder {
                 throw (RuntimeException) t;
             if (t instanceof Error)
                 throw (Error) t;
+            return t;
         }
+        return x;
     }
 
 
@@ -157,9 +160,10 @@ class ContextFinder {
         } catch (ClassNotFoundException x) {
             throw new JAXBException(Messages.format(Messages.PROVIDER_NOT_FOUND, className), x);
 
-        } catch (RuntimeException x) {
+        } catch (RuntimeException | JAXBException x) {
             // avoid wrapping RuntimeException to JAXBException,
             // because it indicates a bug in this code.
+            // JAXBException re-thrown as is
             throw x;
         } catch (Exception x) {
             // can't catch JAXBException because the method is hidden behind
@@ -189,8 +193,9 @@ class ContextFinder {
             try {
                 Method m = spFactory.getMethod("createContext", String.class, ClassLoader.class, Map.class);
                 // any failure in invoking this method would be considered fatal
-                context = m.invoke(null, contextPath, classLoader, properties);
-            } catch (NoSuchMethodException e) {
+                Object obj = instantiateProviderIfNecessary(m);
+                context = m.invoke(obj, contextPath, classLoader, properties);
+            } catch (NoSuchMethodException ignored) {
                 // it's not an error for the provider not to have this method.
             }
 
@@ -198,8 +203,9 @@ class ContextFinder {
                 // try the old method that doesn't take properties. compatible with 1.0.
                 // it is an error for an implementation not to have both forms of the createContext method.
                 Method m = spFactory.getMethod("createContext", String.class, ClassLoader.class);
+                Object obj = instantiateProviderIfNecessary(m);
                 // any failure in invoking this method would be considered fatal
-                context = m.invoke(null, contextPath, classLoader);
+                context = m.invoke(obj, contextPath, classLoader);
             }
 
             if (!(context instanceof JAXBContext)) {
@@ -208,18 +214,11 @@ class ContextFinder {
             }
             return (JAXBContext) context;
         } catch (InvocationTargetException x) {
-            handleInvocationTargetException(x);
-            // for other exceptions, wrap the internal target exception
-            // with a JAXBException
-            Throwable e = x;
-            if (x.getTargetException() != null)
-                e = x.getTargetException();
-
+            // throw if it is exception not to be wrapped
+            // otherwise, wrap with a JAXBException
+            Throwable e = handleInvocationTargetException(x);
             throw new JAXBException(Messages.format(Messages.COULD_NOT_INSTANTIATE, spFactory, e), e);
-        } catch (RuntimeException x) {
-            // avoid wrapping RuntimeException to JAXBException,
-            // because it indicates a bug in this code.
-            throw x;
+
         } catch (Exception x) {
             // can't catch JAXBException because the method is hidden behind
             // reflection.  Root element collisions detected in the call to
@@ -229,6 +228,23 @@ class ContextFinder {
         }
     }
 
+    private static Object instantiateProviderIfNecessary(Method m) throws JAXBException {
+        Class<?> declaringClass = m.getDeclaringClass();
+        try {
+            if (JAXBContextFactory.class.isAssignableFrom(declaringClass)) {
+                return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
+                    @Override
+                    public Object run() throws Exception {
+                        return declaringClass.newInstance();
+                    }
+                });
+            }
+            return null;
+        } catch (PrivilegedActionException e) {
+            throw new JAXBException(Messages.format(Messages.COULD_NOT_INSTANTIATE, declaringClass, e), e);
+        }
+    }
+
     /**
      * Create an instance of a class using the thread context ClassLoader
      */
@@ -255,7 +271,8 @@ class ContextFinder {
         try {
 
             Method m = spFactory.getMethod("createContext", Class[].class, Map.class);
-            Object context = m.invoke(null, classes, properties);
+            Object obj = instantiateProviderIfNecessary(m);
+            Object context = m.invoke(obj, classes, properties);
             if (!(context instanceof JAXBContext)) {
                 // the cast would fail, so generate an exception with a nice message
                 throw handleClassCastException(context.getClass(), JAXBContext.class);
@@ -264,13 +281,10 @@ class ContextFinder {
 
         } catch (NoSuchMethodException | IllegalAccessException e) {
             throw new JAXBException(e);
-
         } catch (InvocationTargetException e) {
-            handleInvocationTargetException(e);
-
-            Throwable x = e;
-            if (e.getTargetException() != null)
-                x = e.getTargetException();
+            // throw if it is exception not to be wrapped
+            // otherwise, wrap with a JAXBException
+            Throwable x = handleInvocationTargetException(e);
 
             throw new JAXBException(x);
         }
diff --git a/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/JAXBContext.java b/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/JAXBContext.java
index c5185236400..adfb479a849 100644
--- a/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/JAXBContext.java
+++ b/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/JAXBContext.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2016, 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
@@ -94,7 +94,7 @@ import java.io.InputStream;
  * allows the merging of global elements and type definitions across a set of schemas (listed
  * in the {@code contextPath}). Since each schema in the schema set can belong
  * to distinct namespaces, the unification of schemas to an unmarshalling
- * context should be namespace independent.  This means that a client
+ * context must be namespace independent.  This means that a client
  * application is able to unmarshal XML documents that are instances of
  * any of the schemas listed in the {@code contextPath}.  For example:
  *
@@ -200,21 +200,28 @@ import java.io.InputStream;
  *
  * <h3>Discovery of JAXB implementation</h3>
  * <p>
- * When one of the {@code newInstance} methods is called, a JAXB implementation is discovered
- * by the following steps.
+ * To create an instance of {@link JAXBContext}, one of {@code JAXBContext.newInstance(...)} methods is invoked. After
+ * JAX-B implementation is discovered, call is delegated to appropriate provider's method {@code createContext(...)}
+ * passing parameters from the original call.
+ * <p>
+ * JAX-B implementation discovery happens each time {@code JAXBContext.newInstance} is invoked. If there is no user
+ * specific configuration provided, default JAX-B provider must be returned.
+ * <p>
+ * Implementation discovery consists of following steps:
  *
  * <ol>
  *
  * <li>
- * For each package/class explicitly passed in to the {@link #newInstance} method, in the order they are specified,
- * {@code jaxb.properties} file is looked up in its package, by using the associated classloader &mdash;
+ * Packages/classes explicitly passed in to the {@link #newInstance} method are processed in the order they are
+ * specified, until {@code jaxb.properties} file is looked up in its package, by using the associated classloader &mdash;
  * this is {@link Class#getClassLoader() the owner class loader} for a {@link Class} argument, and for a package
  * the specified {@link ClassLoader}.
  *
  * <p>
- * If such a file is discovered, it is {@link Properties#load(InputStream) loaded} as a property file, and
- * the value of the {@link #JAXB_CONTEXT_FACTORY} key will be assumed to be the provider factory class.
- * This class is then loaded by the associated class loader discussed above.
+ * If such a resource is discovered, it is {@link Properties#load(InputStream) loaded} as a property file, and
+ * the value of the {@link #JAXB_CONTEXT_FACTORY} key will be assumed to be the provider factory class. If no value
+ * found, {@code "javax.xml.bind.context.factory"} is used as a key for backwards compatibility reasons. This class is
+ * then loaded by the associated class loader discussed above.
  *
  * <p>
  * This phase of the look up allows some packages to force the use of a certain JAXB implementation.
@@ -222,7 +229,9 @@ import java.io.InputStream;
  *
  * <li>
  * If the system property {@link #JAXB_CONTEXT_FACTORY} exists, then its value is assumed to be the provider
- * factory class. This phase of the look up enables per-JVM override of the JAXB implementation.
+ * factory class. If no such property exists, properties {@code "javax.xml.bind.context.factory"} and
+ * {@code "javax.xml.bind.JAXBContext"} are checked too (in this order), for backwards compatibility reasons. This phase
+ * of the look up enables per-JVM override of the JAXB implementation.
  *
  * <li>
  * Provider of {@link javax.xml.bind.JAXBContextFactory} is loaded using the service-provider loading
@@ -235,43 +244,58 @@ import java.io.InputStream;
  * <br>
  * In case of {@link java.util.ServiceConfigurationError service
  * configuration error} a {@link javax.xml.bind.JAXBException} will be thrown.
- * </li>
  *
  * <li>
  * Look for resource {@code /META-INF/services/javax.xml.bind.JAXBContext} using provided class loader.
  * Methods without class loader parameter use {@code Thread.currentThread().getContextClassLoader()}.
- * If such a resource exists, its content is assumed to be the provider factory class and must supply
- * an implementation class containing the following method signatures:
+ * If such a resource exists, its content is assumed to be the provider factory class.
  *
- * <pre>
- *
- * public static JAXBContext createContext(
- *                                      String contextPath,
- *                                      ClassLoader classLoader,
- *                                      Map&lt;String,Object&gt; properties throws JAXBException
- *
- * public static JAXBContext createContext(
- *                                      Class[] classes,
- *                                      Map&lt;String,Object&gt; properties ) throws JAXBException
- * </pre>
  * This configuration method is deprecated.
  *
  * <li>
  * Finally, if all the steps above fail, then the rest of the look up is unspecified. That said,
  * the recommended behavior is to simply look for some hard-coded platform default JAXB implementation.
- * This phase of the look up is so that JavaSE can have its own JAXB implementation as the last resort.
+ * This phase of the look up is so that Java SE can have its own JAXB implementation as the last resort.
  * </ol>
  *
  * <p>
- * Once the provider factory class {@link javax.xml.bind.JAXBContextFactory} is discovered, one of its methods
- * {@link javax.xml.bind.JAXBContextFactory#createContext(String, ClassLoader, java.util.Map)} or
- * {@link javax.xml.bind.JAXBContextFactory#createContext(Class[], java.util.Map)} is invoked
- * to create a {@link JAXBContext}.
+ * Once the provider factory class is discovered, context creation is delegated to one of its
+ * {@code createContext(...)} methods.
+ *
+ * For backward compatibility reasons, there are two ways how to implement provider factory class:
+ * <ol>
+ *     <li>the class is implementation of {@link javax.xml.bind.JAXBContextFactory}. It must also implement no-arg
+ *     constructor. If discovered in other step then 3, new instance using no-arg constructor is created first.
+ *     After that, appropriate instance method is invoked on this instance.
+ *     <li>the class is not implementation of interface above and then it is mandated to implement the following
+ *     static method signatures:
+ * <pre>
+ *
+ * public static JAXBContext createContext(
+ *                                      String contextPath,
+ *                                      ClassLoader classLoader,
+ *                                      Map&lt;String,Object&gt; properties ) throws JAXBException
+ *
+ * public static JAXBContext createContext(
+ *                                      Class[] classes,
+ *                                      Map&lt;String,Object&gt; properties ) throws JAXBException
+ * </pre>
+ *      In this scenario, appropriate static method is used instead of instance method. This approach is incompatible
+ *      with {@link java.util.ServiceLoader} so it can't be used with step 3.
+ * </ol>
+ * <p>
+ * There is no difference in behavior of given method {@code createContext(...)} regardless of whether it uses approach
+ * 1 (JAXBContextFactory) or 2 (no interface, static methods).
  *
  * @apiNote
- * <p>Service discovery method using file /META-INF/services/javax.xml.bind.JAXBContext (described in step 4)
- * and leveraging provider's static methods is supported only to allow backwards compatibility, but it is strongly
- * recommended to migrate to standard ServiceLoader mechanism (described in step 3).
+ * Service discovery method using resource {@code /META-INF/services/javax.xml.bind.JAXBContext} (described in step 4)
+ * is supported only to allow backwards compatibility, it is strongly recommended to migrate to standard
+ * {@link java.util.ServiceLoader} mechanism (described in step 3). The difference here is the resource name, which
+ * doesn't match service's type name.
+ * <p>
+ * Also using providers implementing interface {@link JAXBContextFactory} is preferred over using ones defining
+ * static methods, same as {@link JAXBContext#JAXB_CONTEXT_FACTORY} property is preferred over property
+ * {@code "javax.xml.bind.context.factory"}
  *
  * @implNote
  * Within the last step, if Glassfish AS environment detected, its specific service loader is used to find factory class.
@@ -308,16 +332,10 @@ public abstract class JAXBContext {
      * the context class loader of the current thread.
      *
      * @throws JAXBException if an error was encountered while creating the
-     *                       {@code JAXBContext} such as
-     * <ol>
-     *   <li>failure to locate either ObjectFactory.class or jaxb.index in the packages</li>
-     *   <li>an ambiguity among global elements contained in the contextPath</li>
-     *   <li>failure to locate a value for the context factory provider property</li>
-     *   <li>mixing schema derived packages from different providers on the same contextPath</li>
-     * </ol>
+     *               {@code JAXBContext}. See {@link JAXBContext#newInstance(String, ClassLoader, Map)} for details.
      */
     public static JAXBContext newInstance( String contextPath )
-        throws JAXBException {
+            throws JAXBException {
 
         //return newInstance( contextPath, JAXBContext.class.getClassLoader() );
         return newInstance( contextPath, getContextClassLoader());
@@ -405,13 +423,7 @@ public abstract class JAXBContext {
      *
      * @return a new instance of a {@code JAXBContext}
      * @throws JAXBException if an error was encountered while creating the
-     *                       {@code JAXBContext} such as
-     * <ol>
-     *   <li>failure to locate either ObjectFactory.class or jaxb.index in the packages</li>
-     *   <li>an ambiguity among global elements contained in the contextPath</li>
-     *   <li>failure to locate a value for the context factory provider property</li>
-     *   <li>mixing schema derived packages from different providers on the same contextPath</li>
-     * </ol>
+     *               {@code JAXBContext}. See {@link JAXBContext#newInstance(String, ClassLoader, Map)} for details.
      */
     public static JAXBContext newInstance( String contextPath, ClassLoader classLoader ) throws JAXBException {
 
@@ -427,7 +439,7 @@ public abstract class JAXBContext {
      * the instantiation of {@link JAXBContext}.
      *
      * <p>
-     * The interpretation of properties is up to implementations. Implementations should
+     * The interpretation of properties is up to implementations. Implementations must
      * throw {@code JAXBException} if it finds properties that it doesn't understand.
      *
      * @param contextPath list of java package names that contain schema derived classes
@@ -439,13 +451,7 @@ public abstract class JAXBContext {
      *
      * @return a new instance of a {@code JAXBContext}
      * @throws JAXBException if an error was encountered while creating the
-     *                       {@code JAXBContext} such as
-     * <ol>
-     *   <li>failure to locate either ObjectFactory.class or jaxb.index in the packages</li>
-     *   <li>an ambiguity among global elements contained in the contextPath</li>
-     *   <li>failure to locate a value for the context factory provider property</li>
-     *   <li>mixing schema derived packages from different providers on the same contextPath</li>
-     * </ol>
+     *                       {@code JAXBContext}. See {@link #newInstance(String, ClassLoader)} for details.
      * @since 1.6, JAXB 2.0
      */
     public static JAXBContext newInstance( String contextPath,
@@ -454,14 +460,14 @@ public abstract class JAXBContext {
 
         return ContextFinder.find(
                         /* The default property name according to the JAXB spec */
-                        JAXB_CONTEXT_FACTORY,
+                JAXB_CONTEXT_FACTORY,
 
                         /* the context path supplied by the client app */
-                        contextPath,
+                contextPath,
 
                         /* class loader to be used */
-                        classLoader,
-                        properties );
+                classLoader,
+                properties );
     }
 
 // TODO: resurrect this once we introduce external annotations
@@ -583,17 +589,8 @@ public abstract class JAXBContext {
      * @return
      *      A new instance of a {@code JAXBContext}.
      *
-     * @throws JAXBException
-     *      if an error was encountered while creating the
-     *      {@code JAXBContext}, such as (but not limited to):
-     * <ol>
-     *  <li>No JAXB implementation was discovered
-     *  <li>Classes use JAXB annotations incorrectly
-     *  <li>Classes have colliding annotations (i.e., two classes with the same type name)
-     *  <li>The JAXB implementation was unable to locate
-     *      provider-specific out-of-band information (such as additional
-     *      files generated at the development time.)
-     * </ol>
+     * @throws JAXBException if an error was encountered while creating the
+     *               {@code JAXBContext}. See {@link JAXBContext#newInstance(Class[], Map)} for details.
      *
      * @throws IllegalArgumentException
      *      if the parameter contains {@code null} (i.e., {@code newInstance(null);})
@@ -601,7 +598,7 @@ public abstract class JAXBContext {
      * @since 1.6, JAXB 2.0
      */
     public static JAXBContext newInstance( Class<?> ... classesToBeBound )
-        throws JAXBException {
+            throws JAXBException {
 
         return newInstance(classesToBeBound,Collections.<String,Object>emptyMap());
     }
@@ -614,7 +611,7 @@ public abstract class JAXBContext {
      * to configure 'properties' for this instantiation of {@link JAXBContext}.
      *
      * <p>
-     * The interpretation of properties is up to implementations. Implementations should
+     * The interpretation of properties is up to implementations. Implementations must
      * throw {@code JAXBException} if it finds properties that it doesn't understand.
      *
      * @param classesToBeBound
@@ -646,10 +643,10 @@ public abstract class JAXBContext {
      * @since 1.6, JAXB 2.0
      */
     public static JAXBContext newInstance( Class<?>[] classesToBeBound, Map<String,?> properties )
-        throws JAXBException {
+            throws JAXBException {
 
         if (classesToBeBound == null) {
-                throw new IllegalArgumentException();
+            throw new IllegalArgumentException();
         }
 
         // but it is an error to have nulls in it.
diff --git a/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/JAXBContextFactory.java b/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/JAXBContextFactory.java
index 09630484dc9..ef98dc61e4f 100644
--- a/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/JAXBContextFactory.java
+++ b/jaxws/src/java.xml.bind/share/classes/javax/xml/bind/JAXBContextFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, 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
@@ -56,14 +56,7 @@ public interface JAXBContextFactory {
      *
      * @throws JAXBException
      *      if an error was encountered while creating the
-     *      {@code JAXBContext}, such as (but not limited to):
-     * <ol>
-     *  <li>Classes use JAXB annotations incorrectly
-     *  <li>Classes have colliding annotations (i.e., two classes with the same type name)
-     *  <li>The JAXB implementation was unable to locate
-     *      provider-specific out-of-band information (such as additional
-     *      files generated at the development time.)
-     * </ol>
+     *      {@code JAXBContext}. See {@link JAXBContext#newInstance(Class[], Map)} for details.
      *
      * @throws IllegalArgumentException
      *      if the parameter contains {@code null} (i.e., {@code newInstance(null,someMap);})
@@ -81,7 +74,7 @@ public interface JAXBContextFactory {
      * For semantics see {@link javax.xml.bind.JAXBContext#newInstance(String, ClassLoader, java.util.Map)}
      *
      * <p>
-     * The interpretation of properties is up to implementations. Implementations should
+     * The interpretation of properties is up to implementations. Implementations must
      * throw {@code JAXBException} if it finds properties that it doesn't understand.
      *
      * @param contextPath list of java package names that contain schema derived classes
@@ -93,13 +86,8 @@ public interface JAXBContextFactory {
      *
      * @return a new instance of a {@code JAXBContext}
      * @throws JAXBException if an error was encountered while creating the
-     *                       {@code JAXBContext} such as
-     * <ol>
-     *   <li>failure to locate either ObjectFactory.class or jaxb.index in the packages</li>
-     *   <li>an ambiguity among global elements contained in the contextPath</li>
-     *   <li>failure to locate a value for the context factory provider property</li>
-     *   <li>mixing schema derived packages from different providers on the same contextPath</li>
-     * </ol>
+     *      {@code JAXBContext}. See {@link JAXBContext#newInstance(String, ClassLoader, Map)} for details.
+     *
      * @since 9, JAXB 2.3
      */
     JAXBContext createContext(String contextPath,
diff --git a/jdk/.hgtags b/jdk/.hgtags
index 9f1d47685fb..a0ad6c5e122 100644
--- a/jdk/.hgtags
+++ b/jdk/.hgtags
@@ -345,3 +345,4 @@ e1a789be1535741274c9779f4d4ca3495196b5c3 jdk-9+99
 3d452840f48299a36842760d17c0c8402f0e1266 jdk-9+100
 5e8370fb3ed925335164afe340d1e54beab2d4d5 jdk-9+101
 6eb3c8132e489dab81adde4ce29844904ce15482 jdk-9+102
+eee1ced1d8e78293f2a004af818ca474387dbebf jdk-9+103
diff --git a/jdk/make/launcher/Launcher-jdk.javadoc.gmk b/jdk/make/launcher/Launcher-jdk.javadoc.gmk
index 5922c40d8da..afdb687b0ab 100644
--- a/jdk/make/launcher/Launcher-jdk.javadoc.gmk
+++ b/jdk/make/launcher/Launcher-jdk.javadoc.gmk
@@ -26,7 +26,7 @@
 include LauncherCommon.gmk
 
 $(eval $(call SetupBuildLauncher, javadoc, \
-    MAIN_CLASS := com.sun.tools.javadoc.Main, \
+    MAIN_CLASS := jdk.javadoc.internal.tool.Main, \
     CFLAGS := -DEXPAND_CLASSPATH_WILDCARDS \
         -DNEVER_ACT_AS_SERVER_CLASS_MACHINE, \
 ))
diff --git a/jdk/src/java.base/share/classes/java/lang/Integer.java b/jdk/src/java.base/share/classes/java/lang/Integer.java
index 5302091fb39..2a846c4d07f 100644
--- a/jdk/src/java.base/share/classes/java/lang/Integer.java
+++ b/jdk/src/java.base/share/classes/java/lang/Integer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2016, 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
@@ -339,7 +339,6 @@ public final class Integer extends Number implements Comparable<Integer> {
         // assert shift > 0 && shift <=5 : "Illegal shift value";
         int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val);
         int chars = Math.max(((mag + (shift - 1)) / shift), 1);
-
         if (COMPACT_STRINGS) {
             byte[] buf = new byte[chars];
             formatUnsignedInt(val, shift, buf, 0, chars);
@@ -477,8 +476,13 @@ public final class Integer extends Number implements Comparable<Integer> {
      * values, to cover the Integer.MIN_VALUE case. Converting otherwise
      * (negative to positive) will expose -Integer.MIN_VALUE that overflows
      * integer.
+     *
+     * @param i     value to convert
+     * @param index next index, after the least significant digit
+     * @param buf   target buffer, Latin1-encoded
+     * @return index of the most significant digit or minus sign, if present
      */
-    static void getChars(int i, int index, byte[] buf) {
+    static int getChars(int i, int index, byte[] buf) {
         int q, r;
         int charPos = index;
 
@@ -509,9 +513,19 @@ public final class Integer extends Number implements Comparable<Integer> {
         if (negative) {
             buf[--charPos] = (byte)'-';
         }
+        return charPos;
     }
 
-    static void getCharsUTF16(int i, int index, byte[] buf) {
+    /**
+     * This is a variant of {@link #getChars(int, int, byte[])}, but for
+     * UTF-16 coder.
+     *
+     * @param i     value to convert
+     * @param index next index, after the least significant digit
+     * @param buf   target buffer, UTF16-coded.
+     * @return index of the most significant digit or minus sign, if present
+     */
+    static int getCharsUTF16(int i, int index, byte[] buf) {
         int q, r;
         int charPos = index;
 
@@ -542,6 +556,7 @@ public final class Integer extends Number implements Comparable<Integer> {
         if (negative) {
             StringUTF16.putChar(buf, --charPos, '-');
         }
+        return charPos;
     }
 
     // Left here for compatibility reasons, see JDK-8143900.
diff --git a/jdk/src/java.base/share/classes/java/lang/Long.java b/jdk/src/java.base/share/classes/java/lang/Long.java
index 191ae2071d4..48019cd31b3 100644
--- a/jdk/src/java.base/share/classes/java/lang/Long.java
+++ b/jdk/src/java.base/share/classes/java/lang/Long.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2016, 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
@@ -379,7 +379,6 @@ public final class Long extends Number implements Comparable<Long> {
         // assert shift > 0 && shift <=5 : "Illegal shift value";
         int mag = Long.SIZE - Long.numberOfLeadingZeros(val);
         int chars = Math.max(((mag + (shift - 1)) / shift), 1);
-
         if (COMPACT_STRINGS) {
             byte[] buf = new byte[chars];
             formatUnsignedLong0(val, shift, buf, 0, chars);
@@ -489,8 +488,13 @@ public final class Long extends Number implements Comparable<Long> {
      * values, to cover the Long.MIN_VALUE case. Converting otherwise
      * (negative to positive) will expose -Long.MIN_VALUE that overflows
      * long.
+     *
+     * @param i     value to convert
+     * @param index next index, after the least significant digit
+     * @param buf   target buffer, Latin1-encoded
+     * @return index of the most significant digit or minus sign, if present
      */
-    static void getChars(long i, int index, byte[] buf) {
+    static int getChars(long i, int index, byte[] buf) {
         long q;
         int r;
         int charPos = index;
@@ -533,9 +537,19 @@ public final class Long extends Number implements Comparable<Long> {
         if (negative) {
             buf[--charPos] = (byte)'-';
         }
+        return charPos;
     }
 
-    static void getCharsUTF16(long i, int index, byte[] buf) {
+    /**
+     * This is a variant of {@link #getChars(long, int, byte[])}, but for
+     * UTF-16 coder.
+     *
+     * @param i     value to convert
+     * @param index next index, after the least significant digit
+     * @param buf   target buffer, UTF16-coded.
+     * @return index of the most significant digit or minus sign, if present
+     */
+    static int getCharsUTF16(long i, int index, byte[] buf) {
         long q;
         int r;
         int charPos = index;
@@ -578,6 +592,7 @@ public final class Long extends Number implements Comparable<Long> {
         if (negative) {
             StringUTF16.putChar(buf, --charPos, '-');
         }
+        return charPos;
     }
 
     /**
diff --git a/jdk/src/java.base/share/classes/java/lang/StringConcatHelper.java b/jdk/src/java.base/share/classes/java/lang/StringConcatHelper.java
new file mode 100644
index 00000000000..d9517e5226f
--- /dev/null
+++ b/jdk/src/java.base/share/classes/java/lang/StringConcatHelper.java
@@ -0,0 +1,345 @@
+/*
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang;
+
+/**
+ * Helper for string concatenation. These methods are mostly looked up with private lookups
+ * from {@link java.lang.invoke.StringConcatFactory}, and used in {@link java.lang.invoke.MethodHandle}
+ * combinators there.
+ */
+final class StringConcatHelper {
+
+    private StringConcatHelper() {
+        // no instantiation
+    }
+
+    /**
+     * Check for overflow, throw the exception on overflow.
+     * @param len String length
+     * @return length
+     */
+    private static int checkOverflow(int len) {
+        if (len < 0) {
+            throw new OutOfMemoryError("Overflow: String length out of range");
+        }
+        return len;
+    }
+
+    /**
+     * Mix value length into current length
+     * @param current current length
+     * @param value   value to mix in
+     * @return new length
+     */
+    static int mixLen(int current, boolean value) {
+        return checkOverflow(current + (value ? 4 : 5));
+    }
+
+    /**
+     * Mix value length into current length
+     * @param current current length
+     * @param value   value to mix in
+     * @return new length
+     */
+    static int mixLen(int current, byte value) {
+        return mixLen(current, (int)value);
+    }
+
+    /**
+     * Mix value length into current length
+     * @param current current length
+     * @param value   value to mix in
+     * @return new length
+     */
+    static int mixLen(int current, char value) {
+        return checkOverflow(current + 1);
+    }
+
+    /**
+     * Mix value length into current length
+     * @param current current length
+     * @param value   value to mix in
+     * @return new length
+     */
+    static int mixLen(int current, short value) {
+        return mixLen(current, (int)value);
+    }
+
+    /**
+     * Mix value length into current length
+     * @param current current length
+     * @param value   value to mix in
+     * @return new length
+     */
+    static int mixLen(int current, int value) {
+        return checkOverflow(current + Integer.stringSize(value));
+    }
+
+    /**
+     * Mix value length into current length
+     * @param current current length
+     * @param value   value to mix in
+     * @return new length
+     */
+    static int mixLen(int current, long value) {
+        return checkOverflow(current + Long.stringSize(value));
+    }
+
+    /**
+     * Mix value length into current length
+     * @param current current length
+     * @param value   value to mix in
+     * @return new length
+     */
+    static int mixLen(int current, String value) {
+        return checkOverflow(current + value.length());
+    }
+
+    /**
+     * Mix coder into current coder
+     * @param current current coder
+     * @param value   value to mix in
+     * @return new coder
+     */
+    static byte mixCoder(byte current, char value) {
+        return (byte)(current | (StringLatin1.canEncode(value) ? 0 : 1));
+    }
+
+    /**
+     * Mix coder into current coder
+     * @param current current coder
+     * @param value   value to mix in
+     * @return new coder
+     */
+    static byte mixCoder(byte current, String value) {
+        return (byte)(current | value.coder());
+    }
+
+    /**
+     * Mix coder into current coder
+     * @param current current coder
+     * @param value   value to mix in
+     * @return new coder
+     */
+    static byte mixCoder(byte current, boolean value) {
+        // Booleans are represented with Latin1
+        return current;
+    }
+
+    /**
+     * Mix coder into current coder
+     * @param current current coder
+     * @param value   value to mix in
+     * @return new coder
+     */
+    static byte mixCoder(byte current, byte value) {
+        // Bytes are represented with Latin1
+        return current;
+    }
+
+    /**
+     * Mix coder into current coder
+     * @param current current coder
+     * @param value   value to mix in
+     * @return new coder
+     */
+    static byte mixCoder(byte current, short value) {
+        // Shorts are represented with Latin1
+        return current;
+    }
+
+    /**
+     * Mix coder into current coder
+     * @param current current coder
+     * @param value   value to mix in
+     * @return new coder
+     */
+    static byte mixCoder(byte current, int value) {
+        // Ints are represented with Latin1
+        return current;
+    }
+
+    /**
+     * Mix coder into current coder
+     * @param current current coder
+     * @param value   value to mix in
+     * @return new coder
+     */
+    static byte mixCoder(byte current, long value) {
+        // Longs are represented with Latin1
+        return current;
+    }
+
+    /**
+     * Prepends the stringly representation of boolean value into buffer,
+     * given the coder and final index. Index is measured in chars, not in bytes!
+     *
+     * @param index final char index in the buffer
+     * @param buf   buffer to append to
+     * @param coder coder to add with
+     * @param value boolean value to encode
+     * @return new index
+     */
+    static int prepend(int index, byte[] buf, byte coder, boolean value) {
+        if (coder == String.LATIN1) {
+            if (value) {
+                buf[--index] = 'e';
+                buf[--index] = 'u';
+                buf[--index] = 'r';
+                buf[--index] = 't';
+            } else {
+                buf[--index] = 'e';
+                buf[--index] = 's';
+                buf[--index] = 'l';
+                buf[--index] = 'a';
+                buf[--index] = 'f';
+            }
+        } else {
+            if (value) {
+                StringUTF16.putChar(buf, --index, 'e');
+                StringUTF16.putChar(buf, --index, 'u');
+                StringUTF16.putChar(buf, --index, 'r');
+                StringUTF16.putChar(buf, --index, 't');
+            } else {
+                StringUTF16.putChar(buf, --index, 'e');
+                StringUTF16.putChar(buf, --index, 's');
+                StringUTF16.putChar(buf, --index, 'l');
+                StringUTF16.putChar(buf, --index, 'a');
+                StringUTF16.putChar(buf, --index, 'f');
+            }
+        }
+        return index;
+    }
+
+    /**
+     * Prepends the stringly representation of byte value into buffer,
+     * given the coder and final index. Index is measured in chars, not in bytes!
+     *
+     * @param index final char index in the buffer
+     * @param buf   buffer to append to
+     * @param coder coder to add with
+     * @param value byte value to encode
+     * @return new index
+     */
+    static int prepend(int index, byte[] buf, byte coder, byte value) {
+        return prepend(index, buf, coder, (int)value);
+    }
+
+    /**
+     * Prepends the stringly representation of char value into buffer,
+     * given the coder and final index. Index is measured in chars, not in bytes!
+     *
+     * @param index final char index in the buffer
+     * @param buf   buffer to append to
+     * @param coder coder to add with
+     * @param value char value to encode
+     * @return new index
+     */
+    static int prepend(int index, byte[] buf, byte coder, char value) {
+        if (coder == String.LATIN1) {
+            buf[--index] = (byte) (value & 0xFF);
+        } else {
+            StringUTF16.putChar(buf, --index, value);
+        }
+        return index;
+    }
+
+    /**
+     * Prepends the stringly representation of short value into buffer,
+     * given the coder and final index. Index is measured in chars, not in bytes!
+     *
+     * @param index final char index in the buffer
+     * @param buf   buffer to append to
+     * @param coder coder to add with
+     * @param value short value to encode
+     * @return new index
+     */
+    static int prepend(int index, byte[] buf, byte coder, short value) {
+        return prepend(index, buf, coder, (int)value);
+    }
+
+    /**
+     * Prepends the stringly representation of integer value into buffer,
+     * given the coder and final index. Index is measured in chars, not in bytes!
+     *
+     * @param index final char index in the buffer
+     * @param buf   buffer to append to
+     * @param coder coder to add with
+     * @param value integer value to encode
+     * @return new index
+     */
+    static int prepend(int index, byte[] buf, byte coder, int value) {
+        if (coder == String.LATIN1) {
+            return Integer.getChars(value, index, buf);
+        } else {
+            return Integer.getCharsUTF16(value, index, buf);
+        }
+    }
+
+    /**
+     * Prepends the stringly representation of long value into buffer,
+     * given the coder and final index. Index is measured in chars, not in bytes!
+     *
+     * @param index final char index in the buffer
+     * @param buf   buffer to append to
+     * @param coder coder to add with
+     * @param value long value to encode
+     * @return new index
+     */
+    static int prepend(int index, byte[] buf, byte coder, long value) {
+        if (coder == String.LATIN1) {
+            return Long.getChars(value, index, buf);
+        } else {
+            return Long.getCharsUTF16(value, index, buf);
+        }
+    }
+
+    /**
+     * Prepends the stringly representation of String value into buffer,
+     * given the coder and final index. Index is measured in chars, not in bytes!
+     *
+     * @param index final char index in the buffer
+     * @param buf   buffer to append to
+     * @param coder coder to add with
+     * @param value String value to encode
+     * @return new index
+     */
+    static int prepend(int index, byte[] buf, byte coder, String value) {
+        index -= value.length();
+        value.getBytes(buf, index, coder);
+        return index;
+    }
+
+    /**
+     * Instantiates the String with given buffer and coder
+     * @param buf     buffer to use
+     * @param coder   coder to use
+     * @return String resulting string
+     */
+    static String newString(byte[] buf, byte coder) {
+        // Use the private, non-copying constructor (unsafe!)
+        return new String(buf, coder);
+    }
+
+}
diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/StringConcatException.java b/jdk/src/java.base/share/classes/java/lang/invoke/StringConcatException.java
new file mode 100644
index 00000000000..1cf0a66e370
--- /dev/null
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/StringConcatException.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang.invoke;
+
+/**
+ * StringConcatException is thrown by {@link StringConcatFactory} when linkage
+ * invariants are violated.
+ *
+ * @since 9
+ */
+public class StringConcatException extends Exception {
+    private static final long serialVersionUID = 292L + 9L;
+
+    /**
+     * Constructs an exception with a message
+     * @param msg exception message
+     */
+    public StringConcatException(String msg) {
+        super(msg);
+    }
+
+    /**
+     * Constructs an exception with a message and a linked throwable
+     * @param msg   exception message
+     * @param cause throwable cause
+     */
+    public StringConcatException(String msg, Throwable cause) {
+        super(msg, cause);
+    }
+}
diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java b/jdk/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java
new file mode 100644
index 00000000000..016c939a285
--- /dev/null
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java
@@ -0,0 +1,1792 @@
+/*
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang.invoke;
+
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.vm.annotation.ForceInline;
+import sun.misc.Unsafe;
+
+import java.lang.invoke.MethodHandles.Lookup;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.function.Function;
+
+import static jdk.internal.org.objectweb.asm.Opcodes.*;
+
+/**
+ * <p>Methods to facilitate the creation of String concatenation methods, that
+ * can be used to efficiently concatenate a known number of arguments of known
+ * types, possibly after type adaptation and partial evaluation of arguments.
+ * These methods are typically used as <em>bootstrap methods</em> for {@code
+ * invokedynamic} call sites, to support the <em>string concatenation</em>
+ * feature of the Java Programming Language.
+ *
+ * <p>Indirect access to the behavior specified by the provided {@code
+ * MethodHandle} proceeds in order through two phases:
+ *
+ * <ol>
+ *     <li><em>Linkage</em> occurs when the methods in this class are invoked.
+ * They take as arguments a method type describing the concatenated arguments
+ * count and types, and optionally the String <em>recipe</em>, plus the
+ * constants that participate in the String concatenation. The details on
+ * accepted recipe shapes are described further below. Linkage may involve
+ * dynamically loading a new class that implements the expected concatenation
+ * behavior. The {@code CallSite} holds the {@code MethodHandle} pointing to the
+ * exact concatenation method. The concatenation methods may be shared among
+ * different {@code CallSite}s, e.g. if linkage methods produce them as pure
+ * functions.</li>
+ *
+ * <li><em>Invocation</em> occurs when a generated concatenation method is
+ * invoked with the exact dynamic arguments. This may occur many times for a
+ * single concatenation method. The method referenced by the behavior {@code
+ * MethodHandle} is invoked with the static arguments and any additional dynamic
+ * arguments provided on invocation, as if by {@link MethodHandle#invoke(Object...)}.</li>
+ * </ol>
+ *
+ * <p> This class provides two forms of linkage methods: a simple version
+ * ({@link #makeConcat(java.lang.invoke.MethodHandles.Lookup, String,
+ * MethodType)}) using only the dynamic arguments, and an advanced version
+ * ({@link #makeConcatWithConstants(java.lang.invoke.MethodHandles.Lookup,
+ * String, MethodType, String, Object...)} using the advanced forms of capturing
+ * the constant arguments. The advanced strategy can produce marginally better
+ * invocation bytecode, at the expense of exploding the number of shapes of
+ * string concatenation methods present at runtime, because those shapes would
+ * include constant static arguments as well.
+ *
+ * @author Aleksey Shipilev
+ * @author Remi Forax
+ * @author Peter Levart
+ *
+ * @apiNote
+ * <p>There is a JVM limit (classfile structural constraint): no method
+ * can call with more than 255 slots. This limits the number of static and
+ * dynamic arguments one can pass to bootstrap method. Since there are potential
+ * concatenation strategies that use {@code MethodHandle} combinators, we need
+ * to reserve a few empty slots on the parameter lists to to capture the
+ * temporal results. This is why bootstrap methods in this factory do not accept
+ * more than 200 argument slots. Users requiring more than 200 argument slots in
+ * concatenation are expected to split the large concatenation in smaller
+ * expressions.
+ *
+ * @since 9
+ */
+public final class StringConcatFactory {
+
+    /**
+     * Tag used to demarcate an ordinary argument.
+     */
+    private static final char TAG_ARG = '\u0001';
+
+    /**
+     * Tag used to demarcate a constant.
+     */
+    private static final char TAG_CONST = '\u0002';
+
+    /**
+     * Maximum number of argument slots in String Concat call.
+     *
+     * While the maximum number of argument slots that indy call can handle is 253,
+     * we do not use all those slots, to let the strategies with MethodHandle
+     * combinators to use some arguments.
+     */
+    private static final int MAX_INDY_CONCAT_ARG_SLOTS = 200;
+
+    /**
+     * Concatenation strategy to use. See {@link Strategy} for possible options.
+     * This option is controllable with -Djava.lang.invoke.stringConcat JDK option.
+     */
+    private static final Strategy STRATEGY;
+
+    /**
+     * Default strategy to use for concatenation.
+     */
+    private static final Strategy DEFAULT_STRATEGY = Strategy.BC_SB;
+
+    private enum Strategy {
+        /**
+         * Bytecode generator, calling into {@link java.lang.StringBuilder}.
+         */
+        BC_SB,
+
+        /**
+         * Bytecode generator, calling into {@link java.lang.StringBuilder};
+         * but trying to estimate the required storage.
+         */
+        BC_SB_SIZED,
+
+        /**
+         * Bytecode generator, calling into {@link java.lang.StringBuilder};
+         * but computing the required storage exactly.
+         */
+        BC_SB_SIZED_EXACT,
+
+        /**
+         * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}.
+         * This strategy also tries to estimate the required storage.
+         */
+        MH_SB_SIZED,
+
+        /**
+         * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}.
+         * This strategy also estimate the required storage exactly.
+         */
+        MH_SB_SIZED_EXACT,
+
+        /**
+         * MethodHandle-based generator, that constructs its own byte[] array from
+         * the arguments. It computes the required storage exactly.
+         */
+        MH_INLINE_SIZED_EXACT
+    }
+
+    /**
+     * Enables debugging: this may print debugging messages, perform additional (non-neutral for performance)
+     * checks, etc.
+     */
+    private static final boolean DEBUG;
+
+    /**
+     * Enables caching of strategy stubs. This may improve the linkage time by reusing the generated
+     * code, at the expense of contaminating the profiles.
+     */
+    private static final boolean CACHE_ENABLE;
+
+    private static final ConcurrentMap<Key, MethodHandle> CACHE;
+
+    static {
+        // Poke the privileged block once, taking everything we need:
+        final Object[] values = new Object[3];
+        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
+            values[0] = System.getProperty("java.lang.invoke.stringConcat");
+            values[1] = Boolean.getBoolean("java.lang.invoke.stringConcat.cache");
+            values[2] = Boolean.getBoolean("java.lang.invoke.stringConcat.debug");
+            return null;
+        });
+
+        final String strategy = (String)  values[0];
+        CACHE_ENABLE          = (Boolean) values[1];
+        DEBUG                 = (Boolean) values[2];
+
+        STRATEGY = (strategy == null) ? DEFAULT_STRATEGY : Strategy.valueOf(strategy);
+        CACHE = CACHE_ENABLE ? new ConcurrentHashMap<>() : null;
+    }
+
+    private static final class Key {
+        final MethodType mt;
+        final Recipe recipe;
+
+        public Key(MethodType mt, Recipe recipe) {
+            this.mt = mt;
+            this.recipe = recipe;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            Key key = (Key) o;
+
+            if (!mt.equals(key.mt)) return false;
+            if (!recipe.equals(key.recipe)) return false;
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = mt.hashCode();
+            result = 31 * result + recipe.hashCode();
+            return result;
+        }
+    }
+
+    /**
+     * Parses the recipe string, and produces the traversable collection of
+     * {@link java.lang.invoke.StringConcatFactory.RecipeElement}-s for generator
+     * strategies. Notably, this class parses out the constants from the recipe
+     * and from other static arguments.
+     */
+    private static final class Recipe {
+        private final List<RecipeElement> elements;
+        private final List<RecipeElement> elementsRev;
+
+        public Recipe(String src, Object[] constants) {
+            List<RecipeElement> el = new ArrayList<>();
+
+            int constC = 0;
+            int argC = 0;
+
+            StringBuilder acc = new StringBuilder();
+
+            for (int i = 0; i < src.length(); i++) {
+                char c = src.charAt(i);
+
+                if (c == TAG_CONST || c == TAG_ARG) {
+                    // Detected a special tag, flush all accumulated characters
+                    // as a constant first:
+                    if (acc.length() > 0) {
+                        el.add(new RecipeElement(acc.toString()));
+                        acc.setLength(0);
+                    }
+                    if (c == TAG_CONST) {
+                        Object cnst = constants[constC++];
+                        el.add(new RecipeElement(cnst));
+                    }
+                    if (c == TAG_ARG) {
+                        el.add(new RecipeElement(argC++));
+                    }
+                } else {
+                    // Not a special characters, this is a constant embedded into
+                    // the recipe itself.
+                    acc.append(c);
+                }
+            }
+
+            // Flush the remaining characters as constant:
+            if (acc.length() > 0) {
+                el.add(new RecipeElement(acc.toString()));
+            }
+
+            elements = new ArrayList<>(el);
+            Collections.reverse(el);
+            elementsRev = el;
+        }
+
+        public Collection<RecipeElement> getElements() {
+            return elements;
+        }
+
+        public Collection<RecipeElement> getElementsReversed() {
+            return elementsRev;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            Recipe recipe = (Recipe) o;
+            return elements.equals(recipe.elements);
+        }
+
+        @Override
+        public int hashCode() {
+            return elements.hashCode();
+        }
+    }
+
+    private static final class RecipeElement {
+        private final Object value;
+        private final int argPos;
+        private final Tag tag;
+
+        public RecipeElement(Object cnst) {
+            this.value = Objects.requireNonNull(cnst);
+            this.argPos = -1;
+            this.tag = Tag.CONST;
+        }
+
+        public RecipeElement(int arg) {
+            this.value = null;
+            this.argPos = arg;
+            this.tag = Tag.ARG;
+        }
+
+        public Object getValue() {
+            assert (tag == Tag.CONST);
+            return value;
+        }
+
+        public int getArgPos() {
+            assert (tag == Tag.ARG);
+            return argPos;
+        }
+
+        public Tag getTag() {
+            return tag;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            RecipeElement that = (RecipeElement) o;
+
+            if (tag != that.tag) return false;
+            if (tag == Tag.CONST && (!value.equals(that.value))) return false;
+            if (tag == Tag.ARG && (argPos != that.argPos)) return false;
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            return tag.hashCode();
+        }
+    }
+
+    private enum Tag {
+        CONST, ARG
+    }
+
+    /**
+     * Facilitates the creation of optimized String concatenation methods, that
+     * can be used to efficiently concatenate a known number of arguments of
+     * known types, possibly after type adaptation and partial evaluation of
+     * arguments. Typically used as a <em>bootstrap method</em> for {@code
+     * invokedynamic} call sites, to support the <em>string concatenation</em>
+     * feature of the Java Programming Language.
+     *
+     * <p>When the target of the {@code CallSite} returned from this method is
+     * invoked, it returns the result of String concatenation, taking all
+     * function arguments passed to the linkage method as inputs for
+     * concatenation. The target signature is given by {@code concatType}.
+     * The arguments are concatenated as per requirements stated in JLS 15.18.1
+     * "String Concatenation Operator +". Notably, the inputs are converted as
+     * per JLS 5.1.11 "String Conversion", and combined from left to right.
+     *
+     * <p>Assume the linkage arguments are as follows:
+     *
+     * <ul>
+     *     <li>{@code concatType}, describing the {@code CallSite} signature</li>
+     * </ul>
+     *
+     * <p>Then the following linkage invariants must hold:
+     *
+     * <ul>
+     *     <li>The parameter count in {@code concatType} is less than or equal to 200</li>
+     *
+     *     <li>The return type in {@code concatType} is assignable from {@link java.lang.String}</li>
+     * </ul>
+     *
+     * @param lookup   Represents a lookup context with the accessibility
+     *                 privileges of the caller.  When used with {@code
+     *                 invokedynamic}, this is stacked automatically by the VM.
+     * @param name     The name of the method to implement. This name is
+     *                 arbitrary, and has no meaning for this linkage method.
+     *                 When used with {@code invokedynamic}, this is provided by
+     *                 the {@code NameAndType} of the {@code InvokeDynamic}
+     *                 structure and is stacked automatically by the VM.
+     * @param concatType The expected signature of the {@code CallSite}.  The
+     *                   parameter types represent the types of concatenation
+     *                   arguments; the return type is always assignable from {@link
+     *                   java.lang.String}.  When used with {@code invokedynamic},
+     *                   this is provided by the {@code NameAndType} of the {@code
+     *                   InvokeDynamic} structure and is stacked automatically by
+     *                   the VM.
+     * @return a CallSite whose target can be used to perform String
+     * concatenation, with dynamic concatenation arguments described by the given
+     * {@code concatType}.
+     * @throws StringConcatException If any of the linkage invariants described
+     *                               here are violated.
+     * @throws NullPointerException If any of the incoming arguments is null.
+     *                              This will never happen when a bootstrap method
+     *                              is called with invokedynamic.
+     *
+     * @jls  5.1.11 String Conversion
+     * @jls 15.18.1 String Concatenation Operator +
+     */
+    public static CallSite makeConcat(MethodHandles.Lookup lookup,
+                                      String name,
+                                      MethodType concatType) throws StringConcatException {
+        if (DEBUG) {
+            System.out.println("StringConcatFactory " + STRATEGY + " is here for " + concatType);
+        }
+
+        return doStringConcat(lookup, name, concatType, true, null);
+    }
+
+    /**
+     * Facilitates the creation of optimized String concatenation methods, that
+     * can be used to efficiently concatenate a known number of arguments of
+     * known types, possibly after type adaptation and partial evaluation of
+     * arguments. Typically used as a <em>bootstrap method</em> for {@code
+     * invokedynamic} call sites, to support the <em>string concatenation</em>
+     * feature of the Java Programming Language.
+     *
+     * <p>When the target of the {@code CallSite} returned from this method is
+     * invoked, it returns the result of String concatenation, taking all
+     * function arguments and constants passed to the linkage method as inputs for
+     * concatenation. The target signature is given by {@code concatType}, and
+     * does not include constants. The arguments are concatenated as per requirements
+     * stated in JLS 15.18.1 "String Concatenation Operator +". Notably, the inputs
+     * are converted as per JLS 5.1.11 "String Conversion", and combined from left
+     * to right.
+     *
+     * <p>The concatenation <em>recipe</em> is a String description for the way to
+     * construct a concatenated String from the arguments and constants. The
+     * recipe is processed from left to right, and each character represents an
+     * input to concatenation. Recipe characters mean:
+     *
+     * <ul>
+     *
+     *   <li><em>\1 (Unicode point 0001)</em>: an ordinary argument. This
+     *   input is passed through dynamic argument, and is provided during the
+     *   concatenation method invocation. This input can be null.</li>
+     *
+     *   <li><em>\2 (Unicode point 0002):</em> a constant. This input passed
+     *   through static bootstrap argument. This constant can be any value
+     *   representable in constant pool. If necessary, the factory would call
+     *   {@code toString} to perform a one-time String conversion.</li>
+     *
+     *   <li><em>Any other char value:</em> a single character constant.</li>
+     * </ul>
+     *
+     * <p>Assume the linkage arguments are as follows:
+     *
+     * <ul>
+     *   <li>{@code concatType}, describing the {@code CallSite} signature</li>
+     *   <li>{@code recipe}, describing the String recipe</li>
+     *   <li>{@code constants}, the vararg array of constants</li>
+     * </ul>
+     *
+     * <p>Then the following linkage invariants must hold:
+     *
+     * <ul>
+     *   <li>The parameter count in {@code concatType} is less than or equal to
+     *   200</li>
+     *
+     *   <li>The parameter count in {@code concatType} equals to number of \1 tags
+     *   in {@code recipe}</li>
+     *
+     *   <li>The return type in {@code concatType} is assignable
+     *   from {@link java.lang.String}, and matches the return type of the
+     *   returned {@link MethodHandle}</li>
+     *
+     *   <li>The number of elements in {@code constants} equals to number of \2
+     *   tags in {@code recipe}</li>
+     * </ul>
+     *
+     * @param lookup    Represents a lookup context with the accessibility
+     *                  privileges of the caller. When used with {@code
+     *                  invokedynamic}, this is stacked automatically by the
+     *                  VM.
+     * @param name      The name of the method to implement. This name is
+     *                  arbitrary, and has no meaning for this linkage method.
+     *                  When used with {@code invokedynamic}, this is provided
+     *                  by the {@code NameAndType} of the {@code InvokeDynamic}
+     *                  structure and is stacked automatically by the VM.
+     * @param concatType The expected signature of the {@code CallSite}.  The
+     *                  parameter types represent the types of dynamic concatenation
+     *                  arguments; the return type is always assignable from {@link
+     *                  java.lang.String}.  When used with {@code
+     *                  invokedynamic}, this is provided by the {@code
+     *                  NameAndType} of the {@code InvokeDynamic} structure and
+     *                  is stacked automatically by the VM.
+     * @param recipe    Concatenation recipe, described above.
+     * @param constants A vararg parameter representing the constants passed to
+     *                  the linkage method.
+     * @return a CallSite whose target can be used to perform String
+     * concatenation, with dynamic concatenation arguments described by the given
+     * {@code concatType}.
+     * @throws StringConcatException If any of the linkage invariants described
+     *                               here are violated.
+     * @throws NullPointerException If any of the incoming arguments is null, or
+     *                              any constant in {@code recipe} is null.
+     *                              This will never happen when a bootstrap method
+     *                              is called with invokedynamic.
+     * @apiNote Code generators have three distinct ways to process a constant
+     * string operand S in a string concatenation expression.  First, S can be
+     * materialized as a reference (using ldc) and passed as an ordinary argument
+     * (recipe '\1'). Or, S can be stored in the constant pool and passed as a
+     * constant (recipe '\2') . Finally, if S contains neither of the recipe
+     * tag characters ('\1', '\2') then S can be interpolated into the recipe
+     * itself, causing its characters to be inserted into the result.
+     *
+     * @jls  5.1.11 String Conversion
+     * @jls 15.18.1 String Concatenation Operator +
+     */
+    public static CallSite makeConcatWithConstants(MethodHandles.Lookup lookup,
+                                                   String name,
+                                                   MethodType concatType,
+                                                   String recipe,
+                                                   Object... constants) throws StringConcatException {
+        if (DEBUG) {
+            System.out.println("StringConcatFactory " + STRATEGY + " is here for " + concatType + ", {" + recipe + "}, " + Arrays.toString(constants));
+        }
+
+        return doStringConcat(lookup, name, concatType, false, recipe, constants);
+    }
+
+    private static CallSite doStringConcat(MethodHandles.Lookup lookup,
+                                           String name,
+                                           MethodType concatType,
+                                           boolean generateRecipe,
+                                           String recipe,
+                                           Object... constants) throws StringConcatException {
+        Objects.requireNonNull(lookup, "Lookup is null");
+        Objects.requireNonNull(name, "Name is null");
+        Objects.requireNonNull(concatType, "Concat type is null");
+        Objects.requireNonNull(constants, "Constants are null");
+
+        for (Object o : constants) {
+            Objects.requireNonNull(o, "Cannot accept null constants");
+        }
+
+        int cCount = 0;
+        int oCount = 0;
+        if (generateRecipe) {
+            // Mock the recipe to reuse the concat generator code
+            char[] value = new char[concatType.parameterCount()];
+            Arrays.fill(value, TAG_ARG);
+            recipe = new String(value);
+            oCount = concatType.parameterCount();
+        } else {
+            Objects.requireNonNull(recipe, "Recipe is null");
+
+            for (int i = 0; i < recipe.length(); i++) {
+                char c = recipe.charAt(i);
+                if (c == TAG_CONST) cCount++;
+                if (c == TAG_ARG)   oCount++;
+            }
+        }
+
+        if (oCount != concatType.parameterCount()) {
+            throw new StringConcatException(
+                    "Mismatched number of concat arguments: recipe wants " +
+                            oCount +
+                            " arguments, but signature provides " +
+                            concatType.parameterCount());
+        }
+
+        if (cCount != constants.length) {
+            throw new StringConcatException(
+                    "Mismatched number of concat constants: recipe wants " +
+                            cCount +
+                            " constants, but only " +
+                            constants.length +
+                            " are passed");
+        }
+
+        if (!concatType.returnType().isAssignableFrom(String.class)) {
+            throw new StringConcatException(
+                    "The return type should be compatible with String, but it is " +
+                            concatType.returnType());
+        }
+
+        if (concatType.parameterCount() > MAX_INDY_CONCAT_ARG_SLOTS) {
+            throw new StringConcatException("Too many concat argument slots: " +
+                    concatType.parameterCount() +
+                    ", can only accept " +
+                    MAX_INDY_CONCAT_ARG_SLOTS);
+        }
+
+        MethodType mt = adaptType(concatType);
+
+        Recipe rec = new Recipe(recipe, constants);
+
+        MethodHandle mh;
+        if (CACHE_ENABLE) {
+            Key key = new Key(mt, rec);
+            mh = CACHE.get(key);
+            if (mh == null) {
+                mh = generate(lookup, mt, rec);
+                CACHE.put(key, mh);
+            }
+        } else {
+            mh = generate(lookup, mt, rec);
+        }
+        return new ConstantCallSite(mh.asType(concatType));
+    }
+
+    /**
+     * Adapt method type to an API we are going to use.
+     *
+     * This strips the concrete classes from the signatures, thus preventing
+     * class leakage when we cache the concatenation stubs.
+     *
+     * @param args actual argument types
+     * @return argument types the strategy is going to use
+     */
+    private static MethodType adaptType(MethodType args) {
+        Class<?>[] ptypes = args.parameterArray();
+        boolean changed = false;
+        for (int i = 0; i < ptypes.length; i++) {
+            Class<?> ptype = ptypes[i];
+            if (!ptype.isPrimitive() &&
+                    ptype != String.class &&
+                    ptype != Object.class) { // truncate to Object
+                ptypes[i] = Object.class;
+                changed = true;
+            }
+            // else other primitives or String or Object (unchanged)
+        }
+        return changed
+                ? MethodType.methodType(args.returnType(), ptypes)
+                : args;
+    }
+
+    private static MethodHandle generate(Lookup lookup, MethodType mt, Recipe recipe) throws StringConcatException {
+        try {
+            switch (STRATEGY) {
+                case BC_SB:
+                    return BytecodeStringBuilderStrategy.generate(lookup, mt, recipe, Mode.DEFAULT);
+                case BC_SB_SIZED:
+                    return BytecodeStringBuilderStrategy.generate(lookup, mt, recipe, Mode.SIZED);
+                case BC_SB_SIZED_EXACT:
+                    return BytecodeStringBuilderStrategy.generate(lookup, mt, recipe, Mode.SIZED_EXACT);
+                case MH_SB_SIZED:
+                    return MethodHandleStringBuilderStrategy.generate(mt, recipe, Mode.SIZED);
+                case MH_SB_SIZED_EXACT:
+                    return MethodHandleStringBuilderStrategy.generate(mt, recipe, Mode.SIZED_EXACT);
+                case MH_INLINE_SIZED_EXACT:
+                    return MethodHandleInlineCopyStrategy.generate(mt, recipe);
+                default:
+                    throw new StringConcatException("Concatenation strategy " + STRATEGY + " is not implemented");
+            }
+        } catch (Throwable t) {
+            throw new StringConcatException("Generator failed", t);
+        }
+    }
+
+    private enum Mode {
+        DEFAULT(false, false),
+        SIZED(true, false),
+        SIZED_EXACT(true, true);
+
+        private final boolean sized;
+        private final boolean exact;
+
+        Mode(boolean sized, boolean exact) {
+            this.sized = sized;
+            this.exact = exact;
+        }
+
+        boolean isSized() {
+            return sized;
+        }
+
+        boolean isExact() {
+            return exact;
+        }
+    }
+
+    /**
+     * Bytecode StringBuilder strategy.
+     *
+     * <p>This strategy operates in three modes, gated by {@link Mode}.
+     *
+     * <p><b>{@link Strategy#BC_SB}: "bytecode StringBuilder".</b>
+     *
+     * <p>This strategy spins up the bytecode that has the same StringBuilder
+     * chain javac would otherwise emit. This strategy uses only the public API,
+     * and comes as the baseline for the current JDK behavior. On other words,
+     * this strategy moves the javac generated bytecode to runtime. The
+     * generated bytecode is loaded via Unsafe.defineAnonymousClass, but with
+     * the caller class coming from the BSM -- in other words, the protection
+     * guarantees are inherited from the method where invokedynamic was
+     * originally called. This means, among other things, that the bytecode is
+     * verified for all non-JDK uses.
+     *
+     * <p><b>{@link Strategy#BC_SB_SIZED}: "bytecode StringBuilder, but
+     * sized".</b>
+     *
+     * <p>This strategy acts similarly to {@link Strategy#BC_SB}, but it also
+     * tries to guess the capacity required for StringBuilder to accept all
+     * arguments without resizing. This strategy only makes an educated guess:
+     * it only guesses the space required for known types (e.g. primitives and
+     * Strings), but does not otherwise convert arguments. Therefore, the
+     * capacity estimate may be wrong, and StringBuilder may have to
+     * transparently resize or trim when doing the actual concatenation. While
+     * this does not constitute a correctness issue (in the end, that what BC_SB
+     * has to do anyway), this does pose a potential performance problem.
+     *
+     * <p><b>{@link Strategy#BC_SB_SIZED_EXACT}: "bytecode StringBuilder, but
+     * sized exactly".</b>
+     *
+     * <p>This strategy improves on @link Strategy#BC_SB_SIZED}, by first
+     * converting all arguments to String in order to get the exact capacity
+     * StringBuilder should have. The conversion is done via the public
+     * String.valueOf and/or Object.toString methods, and does not touch any
+     * private String API.
+     */
+    private static final class BytecodeStringBuilderStrategy {
+        static final Unsafe UNSAFE = Unsafe.getUnsafe();
+        static final int CLASSFILE_VERSION = 52;
+        static final String NAME_FACTORY = "concat";
+        static final String CLASS_NAME = "java/lang/String$Concat";
+
+        private BytecodeStringBuilderStrategy() {
+            // no instantiation
+        }
+
+        private static MethodHandle generate(MethodHandles.Lookup lookup, MethodType args, Recipe recipe, Mode mode) throws Exception {
+            ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
+
+            cw.visit(CLASSFILE_VERSION,
+                    ACC_SUPER + ACC_PUBLIC + ACC_FINAL + ACC_SYNTHETIC,
+                    CLASS_NAME,
+                    null,
+                    "java/lang/Object",
+                    null
+            );
+
+            MethodVisitor mv = cw.visitMethod(
+                    ACC_PUBLIC + ACC_STATIC + ACC_FINAL,
+                    NAME_FACTORY,
+                    args.toMethodDescriptorString(),
+                    null,
+                    null);
+
+            mv.visitAnnotation("Ljdk/internal/vm/annotation/ForceInline;", true);
+            mv.visitCode();
+
+            Class<?>[] arr = args.parameterArray();
+            boolean[] guaranteedNonNull = new boolean[arr.length];
+
+            if (mode.isExact()) {
+                /*
+                    In exact mode, we need to convert all arguments to their String representations,
+                    as this allows to compute their String sizes exactly. We cannot use private
+                    methods for primitives in here, therefore we need to convert those as well.
+
+                    We also record what arguments are guaranteed to be non-null as the result
+                    of the conversion. String.valueOf does the null checks for us. The only
+                    corner case to take care of is String.valueOf(Object) returning null itself.
+
+                    Also, if any conversion happened, then the slot indices in the incoming
+                    arguments are not equal to the final local maps. The only case this may break
+                    is when converting 2-slot long/double argument to 1-slot String. Therefore,
+                    we get away with tracking modified offset, since no conversion can overwrite
+                    the upcoming the argument.
+                 */
+
+                int off = 0;
+                int modOff = 0;
+                for (int c = 0; c < arr.length; c++) {
+                    Class<?> cl = arr[c];
+                    if (cl == String.class) {
+                        if (off != modOff) {
+                            mv.visitIntInsn(getLoadOpcode(cl), off);
+                            mv.visitIntInsn(ASTORE, modOff);
+                        }
+                    } else {
+                        mv.visitIntInsn(getLoadOpcode(cl), off);
+                        mv.visitMethodInsn(
+                                INVOKESTATIC,
+                                "java/lang/String",
+                                "valueOf",
+                                getStringValueOfDesc(cl),
+                                false
+                        );
+                        mv.visitIntInsn(ASTORE, modOff);
+                        arr[c] = String.class;
+                        guaranteedNonNull[c] = cl.isPrimitive();
+                    }
+                    off += getParameterSize(cl);
+                    modOff += getParameterSize(String.class);
+                }
+            }
+
+            if (mode.isSized()) {
+                /*
+                    When operating in sized mode (this includes exact mode), it makes sense to make
+                    StringBuilder append chains look familiar to OptimizeStringConcat. For that, we
+                    need to do null-checks early, not make the append chain shape simpler.
+                 */
+
+                int off = 0;
+                for (RecipeElement el : recipe.getElements()) {
+                    switch (el.getTag()) {
+                        case CONST: {
+                            // Guaranteed non-null, no null check required.
+                            break;
+                        }
+                        case ARG: {
+                            // Null-checks are needed only for String arguments, and when a previous stage
+                            // did not do implicit null-checks. If a String is null, we eagerly replace it
+                            // with "null" constant. Note, we omit Objects here, because we don't call
+                            // .length() on them down below.
+                            int ac = el.getArgPos();
+                            Class<?> cl = arr[ac];
+                            if (cl == String.class && !guaranteedNonNull[ac]) {
+                                Label l0 = new Label();
+                                mv.visitIntInsn(ALOAD, off);
+                                mv.visitJumpInsn(IFNONNULL, l0);
+                                mv.visitLdcInsn("null");
+                                mv.visitIntInsn(ASTORE, off);
+                                mv.visitLabel(l0);
+                            }
+                            off += getParameterSize(cl);
+                            break;
+                        }
+                        default:
+                            throw new StringConcatException("Unhandled tag: " + el.getTag());
+                    }
+                }
+            }
+
+            // Prepare StringBuilder instance
+            mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
+            mv.visitInsn(DUP);
+
+            if (mode.isSized()) {
+                /*
+                    Sized mode requires us to walk through the arguments, and estimate the final length.
+                    In exact mode, this will operate on Strings only. This code would accumulate the
+                    final length on stack.
+                 */
+                int len = 0;
+                int off = 0;
+
+                mv.visitInsn(ICONST_0);
+
+                for (RecipeElement el : recipe.getElements()) {
+                    switch (el.getTag()) {
+                        case CONST: {
+                            Object cnst = el.getValue();
+                            len += cnst.toString().length();
+                            break;
+                        }
+                        case ARG: {
+                            /*
+                                If an argument is String, then we can call .length() on it. Sized/Exact modes have
+                                converted arguments for us. If an argument is primitive, we can provide a guess
+                                for its String representation size.
+                            */
+                            Class<?> cl = arr[el.getArgPos()];
+                            if (cl == String.class) {
+                                mv.visitIntInsn(ALOAD, off);
+                                mv.visitMethodInsn(
+                                        INVOKEVIRTUAL,
+                                        "java/lang/String",
+                                        "length",
+                                        "()I",
+                                        false
+                                );
+                                mv.visitInsn(IADD);
+                            } else if (cl.isPrimitive()) {
+                                len += estimateSize(cl);
+                            }
+                            off += getParameterSize(cl);
+                            break;
+                        }
+                        default:
+                            throw new StringConcatException("Unhandled tag: " + el.getTag());
+                    }
+                }
+
+                // Constants have non-zero length, mix in
+                if (len > 0) {
+                    iconst(mv, len);
+                    mv.visitInsn(IADD);
+                }
+
+                mv.visitMethodInsn(
+                        INVOKESPECIAL,
+                        "java/lang/StringBuilder",
+                        "<init>",
+                        "(I)V",
+                        false
+                );
+            } else {
+                mv.visitMethodInsn(
+                        INVOKESPECIAL,
+                        "java/lang/StringBuilder",
+                        "<init>",
+                        "()V",
+                        false
+                );
+            }
+
+            // At this point, we have a blank StringBuilder on stack, fill it in with .append calls.
+            {
+                int off = 0;
+                for (RecipeElement el : recipe.getElements()) {
+                    String desc;
+                    switch (el.getTag()) {
+                        case CONST: {
+                            Object cnst = el.getValue();
+                            mv.visitLdcInsn(cnst);
+                            desc = getSBAppendDesc(cnst.getClass());
+                            break;
+                        }
+                        case ARG: {
+                            Class<?> cl = arr[el.getArgPos()];
+                            mv.visitVarInsn(getLoadOpcode(cl), off);
+                            off += getParameterSize(cl);
+                            desc = getSBAppendDesc(cl);
+                            break;
+                        }
+                        default:
+                            throw new StringConcatException("Unhandled tag: " + el.getTag());
+                    }
+                    mv.visitMethodInsn(
+                            INVOKEVIRTUAL,
+                            "java/lang/StringBuilder",
+                            "append",
+                            desc,
+                            false
+                    );
+                }
+            }
+
+            if (DEBUG && mode.isExact()) {
+                /*
+                    Exactness checks compare the final StringBuilder.capacity() with a resulting
+                    String.length(). If these values disagree, that means StringBuilder had to perform
+                    storage trimming, which defeats the purpose of exact strategies.
+                 */
+
+                mv.visitInsn(DUP);
+
+                mv.visitMethodInsn(
+                        INVOKEVIRTUAL,
+                        "java/lang/StringBuilder",
+                        "capacity",
+                        "()I",
+                        false
+                );
+
+                mv.visitIntInsn(ISTORE, 0);
+
+                mv.visitMethodInsn(
+                        INVOKEVIRTUAL,
+                        "java/lang/StringBuilder",
+                        "toString",
+                        "()Ljava/lang/String;",
+                        false
+                );
+
+                mv.visitInsn(DUP);
+
+                mv.visitMethodInsn(
+                        INVOKEVIRTUAL,
+                        "java/lang/String",
+                        "length",
+                        "()I",
+                        false
+                );
+
+                mv.visitIntInsn(ILOAD, 0);
+
+                Label l0 = new Label();
+                mv.visitJumpInsn(IF_ICMPEQ, l0);
+
+                mv.visitTypeInsn(NEW, "java/lang/AssertionError");
+                mv.visitInsn(DUP);
+                mv.visitLdcInsn("Failed exactness check");
+                mv.visitMethodInsn(INVOKESPECIAL,
+                        "java/lang/AssertionError",
+                        "<init>",
+                        "(Ljava/lang/Object;)V",
+                        false);
+                mv.visitInsn(ATHROW);
+
+                mv.visitLabel(l0);
+            } else {
+                mv.visitMethodInsn(
+                        INVOKEVIRTUAL,
+                        "java/lang/StringBuilder",
+                        "toString",
+                        "()Ljava/lang/String;",
+                        false
+                );
+            }
+
+            mv.visitInsn(ARETURN);
+
+            mv.visitMaxs(-1, -1);
+            mv.visitEnd();
+            cw.visitEnd();
+
+            Class<?> targetClass = lookup.lookupClass();
+            final byte[] classBytes = cw.toByteArray();
+            final Class<?> innerClass = UNSAFE.defineAnonymousClass(targetClass, classBytes, null);
+
+            try {
+                UNSAFE.ensureClassInitialized(innerClass);
+                return lookup.findStatic(innerClass, NAME_FACTORY, args);
+            } catch (ReflectiveOperationException e) {
+                throw new StringConcatException("Exception finding constructor", e);
+            }
+        }
+
+        private static String getSBAppendDesc(Class<?> cl) {
+            if (cl.isPrimitive()) {
+                if (cl == Integer.TYPE || cl == Byte.TYPE || cl == Short.TYPE) {
+                    return "(I)Ljava/lang/StringBuilder;";
+                } else if (cl == Boolean.TYPE) {
+                    return "(Z)Ljava/lang/StringBuilder;";
+                } else if (cl == Character.TYPE) {
+                    return "(C)Ljava/lang/StringBuilder;";
+                } else if (cl == Double.TYPE) {
+                    return "(D)Ljava/lang/StringBuilder;";
+                } else if (cl == Float.TYPE) {
+                    return "(F)Ljava/lang/StringBuilder;";
+                } else if (cl == Long.TYPE) {
+                    return "(J)Ljava/lang/StringBuilder;";
+                } else {
+                    throw new IllegalStateException("Unhandled primitive StringBuilder.append: " + cl);
+                }
+            } else if (cl == String.class) {
+                return "(Ljava/lang/String;)Ljava/lang/StringBuilder;";
+            } else {
+                return "(Ljava/lang/Object;)Ljava/lang/StringBuilder;";
+            }
+        }
+
+        private static String getStringValueOfDesc(Class<?> cl) {
+            if (cl.isPrimitive()) {
+                if (cl == Integer.TYPE || cl == Byte.TYPE || cl == Short.TYPE) {
+                    return "(I)Ljava/lang/String;";
+                } else if (cl == Boolean.TYPE) {
+                    return "(Z)Ljava/lang/String;";
+                } else if (cl == Character.TYPE) {
+                    return "(C)Ljava/lang/String;";
+                } else if (cl == Double.TYPE) {
+                    return "(D)Ljava/lang/String;";
+                } else if (cl == Float.TYPE) {
+                    return "(F)Ljava/lang/String;";
+                } else if (cl == Long.TYPE) {
+                    return "(J)Ljava/lang/String;";
+                } else {
+                    throw new IllegalStateException("Unhandled String.valueOf: " + cl);
+                }
+            } else if (cl == String.class) {
+                return "(Ljava/lang/String;)Ljava/lang/String;";
+            } else {
+                return "(Ljava/lang/Object;)Ljava/lang/String;";
+            }
+        }
+
+        /**
+         * The following method is copied from
+         * org.objectweb.asm.commons.InstructionAdapter. Part of ASM: a very small
+         * and fast Java bytecode manipulation framework.
+         * Copyright (c) 2000-2005 INRIA, France Telecom All rights reserved.
+         */
+        private static void iconst(MethodVisitor mv, final int cst) {
+            if (cst >= -1 && cst <= 5) {
+                mv.visitInsn(Opcodes.ICONST_0 + cst);
+            } else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) {
+                mv.visitIntInsn(Opcodes.BIPUSH, cst);
+            } else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) {
+                mv.visitIntInsn(Opcodes.SIPUSH, cst);
+            } else {
+                mv.visitLdcInsn(cst);
+            }
+        }
+
+        private static int getLoadOpcode(Class<?> c) {
+            if (c == Void.TYPE) {
+                throw new InternalError("Unexpected void type of load opcode");
+            }
+            return ILOAD + getOpcodeOffset(c);
+        }
+
+        private static int getOpcodeOffset(Class<?> c) {
+            if (c.isPrimitive()) {
+                if (c == Long.TYPE) {
+                    return 1;
+                } else if (c == Float.TYPE) {
+                    return 2;
+                } else if (c == Double.TYPE) {
+                    return 3;
+                }
+                return 0;
+            } else {
+                return 4;
+            }
+        }
+
+        private static int getParameterSize(Class<?> c) {
+            if (c == Void.TYPE) {
+                return 0;
+            } else if (c == Long.TYPE || c == Double.TYPE) {
+                return 2;
+            }
+            return 1;
+        }
+    }
+
+    /**
+     * MethodHandle StringBuilder strategy.
+     *
+     * <p>This strategy operates in two modes, gated by {@link Mode}.
+     *
+     * <p><b>{@link Strategy#MH_SB_SIZED}: "MethodHandles StringBuilder,
+     * sized".</b>
+     *
+     * <p>This strategy avoids spinning up the bytecode by building the
+     * computation on MethodHandle combinators. The computation is built with
+     * public MethodHandle APIs, resolved from a public Lookup sequence, and
+     * ends up calling the public StringBuilder API. Therefore, this strategy
+     * does not use any private API at all, even the Unsafe.defineAnonymousClass,
+     * since everything is handled under cover by java.lang.invoke APIs.
+     *
+     * <p><b>{@link Strategy#MH_SB_SIZED_EXACT}: "MethodHandles StringBuilder,
+     * sized exactly".</b>
+     *
+     * <p>This strategy improves on @link Strategy#MH_SB_SIZED}, by first
+     * converting all arguments to String in order to get the exact capacity
+     * StringBuilder should have. The conversion is done via the public
+     * String.valueOf and/or Object.toString methods, and does not touch any
+     * private String API.
+     */
+    private static final class MethodHandleStringBuilderStrategy {
+
+        private MethodHandleStringBuilderStrategy() {
+            // no instantiation
+        }
+
+        private static MethodHandle generate(MethodType mt, Recipe recipe, Mode mode) throws Exception {
+            int pc = mt.parameterCount();
+
+            Class<?>[] ptypes = mt.parameterArray();
+            MethodHandle[] filters = new MethodHandle[ptypes.length];
+            for (int i = 0; i < ptypes.length; i++) {
+                MethodHandle filter;
+                switch (mode) {
+                    case SIZED:
+                        // In sized mode, we convert all references and floats/doubles
+                        // to String: there is no specialization for different
+                        // classes in StringBuilder API, and it will convert to
+                        // String internally anyhow.
+                        filter = Stringifiers.forMost(ptypes[i]);
+                        break;
+                    case SIZED_EXACT:
+                        // In exact mode, we convert everything to String:
+                        // this helps to compute the storage exactly.
+                        filter = Stringifiers.forAny(ptypes[i]);
+                        break;
+                    default:
+                        throw new StringConcatException("Not supported");
+                }
+                if (filter != null) {
+                    filters[i] = filter;
+                    ptypes[i] = filter.type().returnType();
+                }
+            }
+
+            List<Class<?>> ptypesList = Arrays.asList(ptypes);
+            MethodHandle[] lengthers = new MethodHandle[pc];
+
+            // Figure out lengths: constants' lengths can be deduced on the spot.
+            // All reference arguments were filtered to String in the combinators below, so we can
+            // call the usual String.length(). Primitive values string sizes can be estimated.
+            int initial = 0;
+            for (RecipeElement el : recipe.getElements()) {
+                switch (el.getTag()) {
+                    case CONST: {
+                        Object cnst = el.getValue();
+                        initial += cnst.toString().length();
+                        break;
+                    }
+                    case ARG: {
+                        final int i = el.getArgPos();
+                        Class<?> type = ptypesList.get(i);
+                        if (type.isPrimitive()) {
+                            MethodHandle est = MethodHandles.constant(int.class, estimateSize(type));
+                            est = MethodHandles.dropArguments(est, 0, type);
+                            lengthers[i] = est;
+                        } else {
+                            lengthers[i] = STRING_LENGTH;
+                        }
+                        break;
+                    }
+                    default:
+                        throw new StringConcatException("Unhandled tag: " + el.getTag());
+                }
+            }
+
+            // Create (StringBuilder, <args>) shape for appending:
+            MethodHandle builder = MethodHandles.dropArguments(MethodHandles.identity(StringBuilder.class), 1, ptypesList);
+
+            // Compose append calls. This is done in reverse because the application order is
+            // reverse as well.
+            for (RecipeElement el : recipe.getElementsReversed()) {
+                MethodHandle appender;
+                switch (el.getTag()) {
+                    case CONST: {
+                        Object constant = el.getValue();
+                        MethodHandle mh = appender(adaptToStringBuilder(constant.getClass()));
+                        appender = MethodHandles.insertArguments(mh, 1, constant);
+                        break;
+                    }
+                    case ARG: {
+                        int ac = el.getArgPos();
+                        appender = appender(ptypesList.get(ac));
+
+                        // Insert dummy arguments to match the prefix in the signature.
+                        // The actual appender argument will be the ac-ith argument.
+                        if (ac != 0) {
+                            appender = MethodHandles.dropArguments(appender, 1, ptypesList.subList(0, ac));
+                        }
+                        break;
+                    }
+                    default:
+                        throw new StringConcatException("Unhandled tag: " + el.getTag());
+                }
+                builder = MethodHandles.foldArguments(builder, appender);
+            }
+
+            // Build the sub-tree that adds the sizes and produces a StringBuilder:
+
+            // a) Start with the reducer that accepts all arguments, plus one
+            //    slot for the initial value. Inject the initial value right away.
+            //    This produces (<ints>)int shape:
+            MethodHandle sum = getReducerFor(pc + 1);
+            MethodHandle adder = MethodHandles.insertArguments(sum, 0, initial);
+
+            // b) Apply lengthers to transform arguments to lengths, producing (<args>)int
+            adder = MethodHandles.filterArguments(adder, 0, lengthers);
+
+            // c) Instantiate StringBuilder (<args>)int -> (<args>)StringBuilder
+            MethodHandle newBuilder = MethodHandles.filterReturnValue(adder, NEW_STRING_BUILDER);
+
+            // d) Fold in StringBuilder constructor, this produces (<args>)StringBuilder
+            MethodHandle mh = MethodHandles.foldArguments(builder, newBuilder);
+
+            // Convert non-primitive arguments to Strings
+            mh = MethodHandles.filterArguments(mh, 0, filters);
+
+            // Convert (<args>)StringBuilder to (<args>)String
+            if (DEBUG && mode.isExact()) {
+                mh = MethodHandles.filterReturnValue(mh, BUILDER_TO_STRING_CHECKED);
+            } else {
+                mh = MethodHandles.filterReturnValue(mh, BUILDER_TO_STRING);
+            }
+
+            return mh;
+        }
+
+        private static MethodHandle getReducerFor(int cnt) {
+            return SUMMERS.computeIfAbsent(cnt, SUMMER);
+        }
+
+        private static MethodHandle appender(Class<?> appendType) {
+            MethodHandle appender = lookupVirtual(MethodHandles.publicLookup(), StringBuilder.class, "append",
+                    StringBuilder.class, adaptToStringBuilder(appendType));
+
+            // appenders should return void, this would not modify the target signature during folding
+            MethodType nt = MethodType.methodType(void.class, StringBuilder.class, appendType);
+            return appender.asType(nt);
+        }
+
+        private static String toStringChecked(StringBuilder sb) {
+            String s = sb.toString();
+            if (s.length() != sb.capacity()) {
+                throw new AssertionError("Exactness check failed: result length = " + s.length() + ", buffer capacity = " + sb.capacity());
+            }
+            return s;
+        }
+
+        private static int sum(int v1, int v2) {
+            return v1 + v2;
+        }
+
+        private static int sum(int v1, int v2, int v3) {
+            return v1 + v2 + v3;
+        }
+
+        private static int sum(int v1, int v2, int v3, int v4) {
+            return v1 + v2 + v3 + v4;
+        }
+
+        private static int sum(int v1, int v2, int v3, int v4, int v5) {
+            return v1 + v2 + v3 + v4 + v5;
+        }
+
+        private static int sum(int v1, int v2, int v3, int v4, int v5, int v6) {
+            return v1 + v2 + v3 + v4 + v5 + v6;
+        }
+
+        private static int sum(int v1, int v2, int v3, int v4, int v5, int v6, int v7) {
+            return v1 + v2 + v3 + v4 + v5 + v6 + v7;
+        }
+
+        private static int sum(int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8) {
+            return v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8;
+        }
+
+        private static int sum(int initial, int[] vs) {
+            int sum = initial;
+            for (int v : vs) {
+                sum += v;
+            }
+            return sum;
+        }
+
+        private static final ConcurrentMap<Integer, MethodHandle> SUMMERS;
+
+        // This one is deliberately non-lambdified to optimize startup time:
+        private static final Function<Integer, MethodHandle> SUMMER = new Function<Integer, MethodHandle>() {
+            @Override
+            public MethodHandle apply(Integer cnt) {
+                if (cnt == 1) {
+                    return MethodHandles.identity(int.class);
+                } else if (cnt <= 8) {
+                    // Variable-arity collectors are not as efficient as small-count methods,
+                    // unroll some initial sizes.
+                    Class<?>[] cls = new Class<?>[cnt];
+                    Arrays.fill(cls, int.class);
+                    return lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, cls);
+                } else {
+                    return lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, int.class, int[].class)
+                            .asCollector(int[].class, cnt - 1);
+                }
+            }
+        };
+
+        private static final MethodHandle NEW_STRING_BUILDER, STRING_LENGTH, BUILDER_TO_STRING, BUILDER_TO_STRING_CHECKED;
+
+        static {
+            SUMMERS = new ConcurrentHashMap<>();
+            Lookup publicLookup = MethodHandles.publicLookup();
+            NEW_STRING_BUILDER = lookupConstructor(publicLookup, StringBuilder.class, int.class);
+            STRING_LENGTH = lookupVirtual(publicLookup, String.class, "length", int.class);
+            BUILDER_TO_STRING = lookupVirtual(publicLookup, StringBuilder.class, "toString", String.class);
+            if (DEBUG) {
+                BUILDER_TO_STRING_CHECKED = lookupStatic(MethodHandles.Lookup.IMPL_LOOKUP,
+                        MethodHandleStringBuilderStrategy.class, "toStringChecked", String.class, StringBuilder.class);
+            } else {
+                BUILDER_TO_STRING_CHECKED = null;
+            }
+        }
+
+    }
+
+
+    /**
+     * <p><b>{@link Strategy#MH_INLINE_SIZED_EXACT}: "MethodHandles inline,
+     * sized exactly".</b>
+     *
+     * <p>This strategy replicates what StringBuilders are doing: it builds the
+     * byte[] array on its own and passes that byte[] array to String
+     * constructor. This strategy requires access to some private APIs in JDK,
+     * most notably, the read-only Integer/Long.stringSize methods that measure
+     * the character length of the integers, and the private String constructor
+     * that accepts byte[] arrays without copying. While this strategy assumes a
+     * particular implementation details for String, this opens the door for
+     * building a very optimal concatenation sequence. This is the only strategy
+     * that requires porting if there are private JDK changes occur.
+     */
+    private static final class MethodHandleInlineCopyStrategy {
+
+        private MethodHandleInlineCopyStrategy() {
+            // no instantiation
+        }
+
+        static MethodHandle generate(MethodType mt, Recipe recipe) throws Throwable {
+
+            // Create filters and obtain filtered parameter types. Filters would be used in the beginning
+            // to convert the incoming arguments into the arguments we can process (e.g. Objects -> Strings).
+            // The filtered argument type list is used all over in the combinators below.
+            Class<?>[] ptypes = mt.parameterArray();
+            MethodHandle[] filters = null;
+            for (int i = 0; i < ptypes.length; i++) {
+                MethodHandle filter = Stringifiers.forMost(ptypes[i]);
+                if (filter != null) {
+                    if (filters == null) {
+                        filters = new MethodHandle[ptypes.length];
+                    }
+                    filters[i] = filter;
+                    ptypes[i] = filter.type().returnType();
+                }
+            }
+            List<Class<?>> ptypesList = Arrays.asList(ptypes);
+
+            // Start building the combinator tree. The tree "starts" with (<parameters>)String, and "finishes"
+            // with the (int, byte[], byte)String in String helper. The combinators are assembled bottom-up,
+            // which makes the code arguably hard to read.
+
+            // Drop all remaining parameter types, leave only helper arguments:
+            MethodHandle mh;
+
+            mh = MethodHandles.dropArguments(NEW_STRING, 2, ptypes);
+            mh = MethodHandles.dropArguments(mh, 0, int.class);
+
+            // In debug mode, check that remaining index is zero.
+            if (DEBUG) {
+                mh = MethodHandles.filterArgument(mh, 0, CHECK_INDEX);
+            }
+
+            // Mix in prependers. This happens when (int, byte[], byte) = (index, storage, coder) is already
+            // known from the combinators below. We are assembling the string backwards, so "index" is the
+            // *ending* index.
+            for (RecipeElement el : recipe.getElements()) {
+                MethodHandle prepender;
+                switch (el.getTag()) {
+                    case CONST: {
+                        Object cnst = el.getValue();
+                        prepender = MethodHandles.insertArguments(prepender(cnst.getClass()), 3, cnst);
+                        break;
+                    }
+                    case ARG: {
+                        int pos = el.getArgPos();
+                        prepender = selectArgument(prepender(ptypesList.get(pos)), 3, ptypesList, pos);
+                        break;
+                    }
+                    default:
+                        throw new StringConcatException("Unhandled tag: " + el.getTag());
+                }
+
+                // Remove "old" index from arguments
+                mh = MethodHandles.dropArguments(mh, 1, int.class);
+
+                // Do the prepend, and put "new" index at index 0
+                mh = MethodHandles.foldArguments(mh, prepender);
+            }
+
+            // Prepare the argument list for prepending. The tree below would instantiate
+            // the storage byte[] into argument 0, so we need to swap "storage" and "index".
+            // The index at this point equals to "size", and resides at argument 1.
+            {
+                MethodType nmt = mh.type()
+                        .changeParameterType(0, byte[].class)
+                        .changeParameterType(1, int.class);
+                mh = MethodHandles.permuteArguments(mh, nmt, swap10(nmt.parameterCount()));
+            }
+
+            // Fold in byte[] instantiation at argument 0.
+            MethodHandle combiner = MethodHandles.dropArguments(NEW_ARRAY, 2, ptypesList);
+            mh = MethodHandles.foldArguments(mh, combiner);
+
+            // Start combining length and coder mixers.
+            //
+            // Length is easy: constant lengths can be computed on the spot, and all non-constant
+            // shapes have been either converted to Strings, or explicit methods for getting the
+            // string length out of primitives are provided.
+            //
+            // Coders are more interesting. Only Object, String and char arguments (and constants)
+            // can have non-Latin1 encoding. It is easier to blindly convert constants to String,
+            // and deduce the coder from there. Arguments would be either converted to Strings
+            // during the initial filtering, or handled by primitive specializations in CODER_MIXERS.
+            //
+            // The method handle shape after all length and coder mixers is:
+            //   (int, byte, <args>)String = ("index", "coder", <args>)
+            byte initialCoder = 0; // initial coder
+            int initialLen = 0;    // initial length, in characters
+            for (RecipeElement el : recipe.getElements()) {
+                switch (el.getTag()) {
+                    case CONST: {
+                        Object constant = el.getValue();
+                        String s = constant.toString();
+                        initialCoder = (byte) coderMixer(String.class).invoke(initialCoder, s);
+                        initialLen += s.length();
+                        break;
+                    }
+                    case ARG: {
+                        int ac = el.getArgPos();
+
+                        Class<?> argClass = ptypesList.get(ac);
+                        MethodHandle lm = selectArgument(lengthMixer(argClass), 1, ptypesList, ac);
+                        lm = MethodHandles.dropArguments(lm, 0, byte.class); // (*)
+                        lm = MethodHandles.dropArguments(lm, 2, byte.class);
+
+                        MethodHandle cm = selectArgument(coderMixer(argClass),  1, ptypesList, ac);
+                        cm = MethodHandles.dropArguments(cm, 0, int.class);  // (**)
+
+                        // Read this bottom up:
+
+                        // 4. Drop old index and coder, producing ("new-index", "new-coder", <args>)
+                        mh = MethodHandles.dropArguments(mh, 2, int.class, byte.class);
+
+                        // 3. Compute "new-index", producing ("new-index", "new-coder", "old-index", "old-coder", <args>)
+                        //    Length mixer ignores both "new-coder" and "old-coder" due to dropArguments above (*)
+                        mh = MethodHandles.foldArguments(mh, lm);
+
+                        // 2. Compute "new-coder", producing ("new-coder", "old-index", "old-coder", <args>)
+                        //    Coder mixer ignores the "old-index" arg due to dropArguments above (**)
+                        mh = MethodHandles.foldArguments(mh, cm);
+
+                        // 1. The mh shape here is ("old-index", "old-coder", <args>)
+                        break;
+                    }
+                    default:
+                        throw new StringConcatException("Unhandled tag: " + el.getTag());
+                }
+            }
+
+            // Insert initial lengths and coders here.
+            // The method handle shape here is (<args>).
+            mh = MethodHandles.insertArguments(mh, 0, initialLen, initialCoder);
+
+            // Apply filters, converting the arguments:
+            if (filters != null) {
+                mh = MethodHandles.filterArguments(mh, 0, filters);
+            }
+
+            return mh;
+        }
+
+        private static int[] swap10(int count) {
+            int[] perm = new int[count];
+            perm[0] = 1;
+            perm[1] = 0;
+            for (int i = 2; i < count; i++) {
+                perm[i] = i;
+            }
+            return perm;
+        }
+
+        // Adapts: (...prefix..., parameter[pos])R -> (...prefix..., ...parameters...)R
+        private static MethodHandle selectArgument(MethodHandle mh, int prefix, List<Class<?>> ptypes, int pos) {
+            if (pos == 0) {
+                return MethodHandles.dropArguments(mh, prefix + 1, ptypes.subList(1, ptypes.size()));
+            } else if (pos == ptypes.size() - 1) {
+                return MethodHandles.dropArguments(mh, prefix, ptypes.subList(0, ptypes.size() - 1));
+            } else { // 0 < pos < ptypes.size() - 1
+                MethodHandle t = MethodHandles.dropArguments(mh, prefix, ptypes.subList(0, pos));
+                return MethodHandles.dropArguments(t, prefix + 1 + pos, ptypes.subList(pos + 1, ptypes.size()));
+            }
+        }
+
+        @ForceInline
+        private static byte[] newArray(int length, byte coder) {
+            return new byte[length << coder];
+        }
+
+        @ForceInline
+        private static int checkIndex(int index) {
+            if (index != 0) {
+                throw new AssertionError("Exactness check failed: " + index + " characters left in the buffer.");
+            }
+            return index;
+        }
+
+        private static MethodHandle prepender(Class<?> cl) {
+            return PREPENDERS.computeIfAbsent(cl, PREPEND);
+        }
+
+        private static MethodHandle coderMixer(Class<?> cl) {
+            return CODER_MIXERS.computeIfAbsent(cl, CODER_MIX);
+        }
+
+        private static MethodHandle lengthMixer(Class<?> cl) {
+            return LENGTH_MIXERS.computeIfAbsent(cl, LENGTH_MIX);
+        }
+
+        // This one is deliberately non-lambdified to optimize startup time:
+        private static final Function<Class<?>, MethodHandle> PREPEND = new Function<Class<?>, MethodHandle>() {
+            @Override
+            public MethodHandle apply(Class<?> c) {
+                return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", int.class, int.class, byte[].class, byte.class, c);
+            }
+        };
+
+        // This one is deliberately non-lambdified to optimize startup time:
+        private static final Function<Class<?>, MethodHandle> CODER_MIX = new Function<Class<?>, MethodHandle>() {
+            @Override
+            public MethodHandle apply(Class<?> c) {
+                return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mixCoder", byte.class, byte.class, c);
+            }
+        };
+
+        // This one is deliberately non-lambdified to optimize startup time:
+        private static final Function<Class<?>, MethodHandle> LENGTH_MIX = new Function<Class<?>, MethodHandle>() {
+            @Override
+            public MethodHandle apply(Class<?> c) {
+                return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mixLen", int.class, int.class, c);
+            }
+        };
+
+        private static final MethodHandle NEW_STRING;
+        private static final MethodHandle CHECK_INDEX;
+        private static final MethodHandle NEW_ARRAY;
+        private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS;
+        private static final ConcurrentMap<Class<?>, MethodHandle> LENGTH_MIXERS;
+        private static final ConcurrentMap<Class<?>, MethodHandle> CODER_MIXERS;
+        private static final Class<?> STRING_HELPER;
+
+        static {
+            try {
+                STRING_HELPER = Class.forName("java.lang.StringConcatHelper");
+            } catch (ClassNotFoundException e) {
+                throw new AssertionError(e);
+            }
+
+            PREPENDERS = new ConcurrentHashMap<>();
+            LENGTH_MIXERS = new ConcurrentHashMap<>();
+            CODER_MIXERS = new ConcurrentHashMap<>();
+
+            NEW_STRING = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newString", String.class, byte[].class, byte.class);
+            NEW_ARRAY  = lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleInlineCopyStrategy.class, "newArray", byte[].class, int.class, byte.class);
+
+            if (DEBUG) {
+                CHECK_INDEX = lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleInlineCopyStrategy.class, "checkIndex", int.class, int.class);
+            } else {
+                CHECK_INDEX = null;
+            }
+        }
+    }
+
+    /**
+     * Public gateways to public "stringify" methods. These methods have the form String apply(T obj), and normally
+     * delegate to {@code String.valueOf}, depending on argument's type.
+     */
+    private static final class Stringifiers {
+        private Stringifiers() {
+            // no instantiation
+        }
+
+        // This one is deliberately non-lambdified to optimize startup time:
+        private static final Function<Class<?>, MethodHandle> MOST = new Function<Class<?>, MethodHandle>() {
+            @Override
+            public MethodHandle apply(Class<?> cl) {
+                MethodHandle mhObject = lookupStatic(Lookup.PUBLIC_LOOKUP, String.class, "valueOf", String.class, Object.class);
+
+                // We need the additional conversion here, because String.valueOf(Object) may return null.
+                // String conversion rules in Java state we need to produce "null" String in this case.
+                // It can be easily done with applying valueOf the second time.
+                MethodHandle mhObjectNoNulls = MethodHandles.filterReturnValue(mhObject,
+                        mhObject.asType(MethodType.methodType(String.class, String.class)));
+
+                if (cl == String.class) {
+                    return mhObject;
+                } else if (cl == float.class) {
+                    return lookupStatic(Lookup.PUBLIC_LOOKUP, String.class, "valueOf", String.class, float.class);
+                } else if (cl == double.class) {
+                    return lookupStatic(Lookup.PUBLIC_LOOKUP, String.class, "valueOf", String.class, double.class);
+                } else if (!cl.isPrimitive()) {
+                    return mhObjectNoNulls;
+                }
+
+                return null;
+            }
+        };
+
+        // This one is deliberately non-lambdified to optimize startup time:
+        private static final Function<Class<?>, MethodHandle> ANY = new Function<Class<?>, MethodHandle>() {
+            @Override
+            public MethodHandle apply(Class<?> cl) {
+                MethodHandle mh = MOST.apply(cl);
+                if (mh != null) {
+                    return mh;
+                }
+
+                if (cl == byte.class || cl == short.class || cl == int.class) {
+                    return lookupStatic(Lookup.PUBLIC_LOOKUP, String.class, "valueOf", String.class, int.class);
+                } else if (cl == boolean.class) {
+                    return lookupStatic(Lookup.PUBLIC_LOOKUP, String.class, "valueOf", String.class, boolean.class);
+                } else if (cl == char.class) {
+                    return lookupStatic(Lookup.PUBLIC_LOOKUP, String.class, "valueOf", String.class, char.class);
+                } else if (cl == long.class) {
+                    return lookupStatic(Lookup.PUBLIC_LOOKUP, String.class, "valueOf", String.class, long.class);
+                } else {
+                    throw new IllegalStateException("Unknown class: " + cl);
+                }
+            }
+        };
+
+        private static final ConcurrentMap<Class<?>, MethodHandle> STRINGIFIERS_MOST = new ConcurrentHashMap<>();
+        private static final ConcurrentMap<Class<?>, MethodHandle> STRINGIFIERS_ANY = new ConcurrentHashMap<>();
+
+        /**
+         * Returns a stringifier for references and floats/doubles only.
+         * Always returns null for other primitives.
+         *
+         * @param t class to stringify
+         * @return stringifier; null, if not available
+         */
+        static MethodHandle forMost(Class<?> t) {
+            return STRINGIFIERS_MOST.computeIfAbsent(t, MOST);
+        }
+
+        /**
+         * Returns a stringifier for any type. Never returns null.
+         *
+         * @param t class to stringify
+         * @return stringifier
+         */
+        static MethodHandle forAny(Class<?> t) {
+            return STRINGIFIERS_ANY.computeIfAbsent(t, ANY);
+        }
+    }
+
+    /* ------------------------------- Common utilities ------------------------------------ */
+
+    private static MethodHandle lookupStatic(Lookup lookup, Class<?> refc, String name, Class<?> rtype, Class<?>... ptypes) {
+        try {
+            return lookup.findStatic(refc, name, MethodType.methodType(rtype, ptypes));
+        } catch (NoSuchMethodException | IllegalAccessException e) {
+            throw new AssertionError(e);
+        }
+    }
+
+    private static MethodHandle lookupVirtual(Lookup lookup, Class<?> refc, String name, Class<?> rtype, Class<?>... ptypes) {
+        try {
+            return lookup.findVirtual(refc, name, MethodType.methodType(rtype, ptypes));
+        } catch (NoSuchMethodException | IllegalAccessException e) {
+            throw new AssertionError(e);
+        }
+    }
+
+    private static MethodHandle lookupConstructor(Lookup lookup, Class<?> refc, Class<?> ptypes) {
+        try {
+            return lookup.findConstructor(refc, MethodType.methodType(void.class, ptypes));
+        } catch (NoSuchMethodException | IllegalAccessException e) {
+            throw new AssertionError(e);
+        }
+    }
+
+    private static int estimateSize(Class<?> cl) {
+        if (cl == Integer.TYPE) {
+            return 11; // "-2147483648"
+        } else if (cl == Boolean.TYPE) {
+            return 5; // "false"
+        } else if (cl == Byte.TYPE) {
+            return 4; // "-128"
+        } else if (cl == Character.TYPE) {
+            return 1; // duh
+        } else if (cl == Short.TYPE) {
+            return 6; // "-32768"
+        } else if (cl == Double.TYPE) {
+            return 26; // apparently, no larger than this, see FloatingDecimal.BinaryToASCIIBuffer.buffer
+        } else if (cl == Float.TYPE) {
+            return 26; // apparently, no larger than this, see FloatingDecimal.BinaryToASCIIBuffer.buffer
+        } else if (cl == Long.TYPE)  {
+            return 20; // "-9223372036854775808"
+        } else {
+            throw new IllegalArgumentException("Cannot estimate the size for " + cl);
+        }
+    }
+
+    private static Class<?> adaptToStringBuilder(Class<?> c) {
+        if (c.isPrimitive()) {
+            if (c == Byte.TYPE || c == Short.TYPE) {
+                return int.class;
+            }
+        } else {
+            if (c != String.class) {
+                return Object.class;
+            }
+        }
+        return c;
+    }
+
+    private StringConcatFactory() {
+        // no instantiation
+    }
+
+}
diff --git a/jdk/src/java.base/share/classes/java/net/URI.java b/jdk/src/java.base/share/classes/java/net/URI.java
index 862588019fd..069aedb685f 100644
--- a/jdk/src/java.base/share/classes/java/net/URI.java
+++ b/jdk/src/java.base/share/classes/java/net/URI.java
@@ -1080,11 +1080,8 @@ public final class URI
      *          If a protocol handler for the URL could not be found,
      *          or if some other error occurred while constructing the URL
      */
-    public URL toURL()
-        throws MalformedURLException {
-        if (!isAbsolute())
-            throw new IllegalArgumentException("URI is not absolute");
-        return new URL(toString());
+    public URL toURL() throws MalformedURLException {
+        return URL.fromURI(this);
     }
 
     // -- Component access methods --
diff --git a/jdk/src/java.base/share/classes/java/net/URL.java b/jdk/src/java.base/share/classes/java/net/URL.java
index 2235a4edd2a..598e4103cf7 100644
--- a/jdk/src/java.base/share/classes/java/net/URL.java
+++ b/jdk/src/java.base/share/classes/java/net/URL.java
@@ -36,6 +36,7 @@ import java.io.ObjectStreamException;
 import java.io.ObjectStreamField;
 import java.io.ObjectInputStream.GetField;
 import java.util.Iterator;
+import java.util.Locale;
 import java.util.NoSuchElementException;
 import java.util.ServiceConfigurationError;
 import java.util.ServiceLoader;
@@ -405,7 +406,7 @@ public final class URL implements java.io.Serializable {
             }
         }
 
-        protocol = protocol.toLowerCase();
+        protocol = protocol.toLowerCase(Locale.ROOT);
         this.protocol = protocol;
         if (host != null) {
 
@@ -579,8 +580,7 @@ public final class URL implements java.io.Serializable {
             for (i = start ; !aRef && (i < limit) &&
                      ((c = spec.charAt(i)) != '/') ; i++) {
                 if (c == ':') {
-
-                    String s = spec.substring(start, i).toLowerCase();
+                    String s = spec.substring(start, i).toLowerCase(Locale.ROOT);
                     if (isValidProtocol(s)) {
                         newProtocol = s;
                         start = i + 1;
@@ -659,6 +659,44 @@ public final class URL implements java.io.Serializable {
         }
     }
 
+    /**
+     * Creates a URL from a URI, as if by invoking {@code uri.toURL()}.
+     *
+     * @see java.net.URI#toURL()
+     */
+    static URL fromURI(URI uri) throws MalformedURLException {
+        if (!uri.isAbsolute()) {
+            throw new IllegalArgumentException("URI is not absolute");
+        }
+        String protocol = uri.getScheme();
+
+        // In general we need to go via Handler.parseURL, but for the jrt
+        // protocol we enforce that the Handler is not overrideable and can
+        // optimize URI to URL conversion.
+        //
+        // Case-sensitive comparison for performance; malformed protocols will
+        // be handled correctly by the slow path.
+        if (protocol.equals("jrt") && !uri.isOpaque()
+                && uri.getRawFragment() == null) {
+
+            String query = uri.getRawQuery();
+            String path = uri.getRawPath();
+            String file = (query == null) ? path : path + "?" + query;
+
+            // URL represent undefined host as empty string while URI use null
+            String host = uri.getHost();
+            if (host == null) {
+                host = "";
+            }
+
+            int port = uri.getPort();
+
+            return new URL("jrt", host, port, file, null);
+        } else {
+            return new URL((URL)null, uri.toString(), null);
+        }
+    }
+
     /*
      * Returns true if specified string is a valid protocol name.
      */
@@ -1275,11 +1313,28 @@ public final class URL implements java.io.Serializable {
         }
     }
 
-    private static final String[] NON_OVERRIDEABLE_PROTOCOLS = {"file", "jrt"};
-    private static boolean isOverrideable(String protocol) {
-        for (String p : NON_OVERRIDEABLE_PROTOCOLS)
-            if (protocol.equalsIgnoreCase(p))
+
+    /**
+     * Non-overrideable protocols: "jrt" and "file"
+     *
+     * Character-based comparison for performance reasons; also ensures
+     * case-insensitive comparison in a locale-independent fashion.
+     */
+    static boolean isOverrideable(String protocol) {
+        if (protocol.length() == 3) {
+            if ((Character.toLowerCase(protocol.charAt(0)) == 'j') &&
+                    (Character.toLowerCase(protocol.charAt(1)) == 'r') &&
+                    (Character.toLowerCase(protocol.charAt(2)) == 't')) {
                 return false;
+            }
+        } else if (protocol.length() == 4) {
+            if ((Character.toLowerCase(protocol.charAt(0)) == 'f') &&
+                    (Character.toLowerCase(protocol.charAt(1)) == 'i') &&
+                    (Character.toLowerCase(protocol.charAt(2)) == 'l') &&
+                    (Character.toLowerCase(protocol.charAt(3)) == 'e')) {
+                return false;
+            }
+        }
         return true;
     }
 
diff --git a/jdk/src/java.base/share/classes/java/nio/file/attribute/FileOwnerAttributeView.java b/jdk/src/java.base/share/classes/java/nio/file/attribute/FileOwnerAttributeView.java
index 9acde225339..82d0d1bce6f 100644
--- a/jdk/src/java.base/share/classes/java/nio/file/attribute/FileOwnerAttributeView.java
+++ b/jdk/src/java.base/share/classes/java/nio/file/attribute/FileOwnerAttributeView.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2016, 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,7 +59,7 @@ public interface FileOwnerAttributeView
     /**
      * Read the file owner.
      *
-     * <p> It it implementation specific if the file owner can be a {@link
+     * <p> It is implementation specific if the file owner can be a {@link
      * GroupPrincipal group}.
      *
      * @return  the file owner
@@ -78,7 +78,7 @@ public interface FileOwnerAttributeView
     /**
      * Updates the file owner.
      *
-     * <p> It it implementation specific if the file owner can be a {@link
+     * <p> It is implementation specific if the file owner can be a {@link
      * GroupPrincipal group}. To ensure consistent and correct behavior
      * across platforms it is recommended that this method should only be used
      * to set the file owner to a user principal that is not a group.
diff --git a/jdk/src/java.base/share/classes/java/util/Map.java b/jdk/src/java.base/share/classes/java/util/Map.java
index 0967523eba3..615bb0a5344 100644
--- a/jdk/src/java.base/share/classes/java/util/Map.java
+++ b/jdk/src/java.base/share/classes/java/util/Map.java
@@ -649,7 +649,7 @@ public interface Map<K, V> {
             try {
                 k = entry.getKey();
                 v = entry.getValue();
-            } catch(IllegalStateException ise) {
+            } catch (IllegalStateException ise) {
                 // this usually means the entry is no longer in the map.
                 throw new ConcurrentModificationException(ise);
             }
@@ -704,7 +704,7 @@ public interface Map<K, V> {
             try {
                 k = entry.getKey();
                 v = entry.getValue();
-            } catch(IllegalStateException ise) {
+            } catch (IllegalStateException ise) {
                 // this usually means the entry is no longer in the map.
                 throw new ConcurrentModificationException(ise);
             }
@@ -714,7 +714,7 @@ public interface Map<K, V> {
 
             try {
                 entry.setValue(v);
-            } catch(IllegalStateException ise) {
+            } catch (IllegalStateException ise) {
                 // this usually means the entry is no longer in the map.
                 throw new ConcurrentModificationException(ise);
             }
@@ -887,7 +887,7 @@ public interface Map<K, V> {
      * or atomicity properties of this method. Any implementation providing
      * atomicity guarantees must override this method and document its
      * concurrency properties.
-      *
+     *
      * @param key key with which the specified value is associated
      * @param value value to be associated with the specified key
      * @return the previous value associated with the specified key, or
@@ -984,6 +984,9 @@ public interface Map<K, V> {
      * @throws ClassCastException if the class of the specified key or value
      *         prevents it from being stored in this map
      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
+     * @throws IllegalArgumentException if some property of the specified key
+     *         or value prevents it from being stored in this map
+     *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
      * @since 1.8
      */
     default V computeIfAbsent(K key,
@@ -1058,6 +1061,9 @@ public interface Map<K, V> {
      * @throws ClassCastException if the class of the specified key or value
      *         prevents it from being stored in this map
      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
+     * @throws IllegalArgumentException if some property of the specified key
+     *         or value prevents it from being stored in this map
+     *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
      * @since 1.8
      */
     default V computeIfPresent(K key,
@@ -1103,7 +1109,7 @@ public interface Map<K, V> {
      * <pre> {@code
      * V oldValue = map.get(key);
      * V newValue = remappingFunction.apply(key, oldValue);
-     * if (oldValue != null ) {
+     * if (oldValue != null) {
      *    if (newValue != null)
      *       map.put(key, newValue);
      *    else
@@ -1147,6 +1153,9 @@ public interface Map<K, V> {
      * @throws ClassCastException if the class of the specified key or value
      *         prevents it from being stored in this map
      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
+     * @throws IllegalArgumentException if some property of the specified key
+     *         or value prevents it from being stored in this map
+     *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
      * @since 1.8
      */
     default V compute(K key,
@@ -1239,6 +1248,9 @@ public interface Map<K, V> {
      * @throws ClassCastException if the class of the specified key or value
      *         prevents it from being stored in this map
      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
+     * @throws IllegalArgumentException if some property of the specified key
+     *         or value prevents it from being stored in this map
+     *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
      * @throws NullPointerException if the specified key is null and this map
      *         does not support null keys or the value or remappingFunction is
      *         null
@@ -1251,7 +1263,7 @@ public interface Map<K, V> {
         V oldValue = get(key);
         V newValue = (oldValue == null) ? value :
                    remappingFunction.apply(oldValue, value);
-        if(newValue == null) {
+        if (newValue == null) {
             remove(key);
         } else {
             put(key, newValue);
diff --git a/jdk/src/java.base/share/classes/java/util/Queue.java b/jdk/src/java.base/share/classes/java/util/Queue.java
index b5456b99f2f..7d5e39c7030 100644
--- a/jdk/src/java.base/share/classes/java/util/Queue.java
+++ b/jdk/src/java.base/share/classes/java/util/Queue.java
@@ -129,14 +129,6 @@ package java.util;
  * <a href="{@docRoot}/../technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
- * @see java.util.Collection
- * @see LinkedList
- * @see PriorityQueue
- * @see java.util.concurrent.LinkedBlockingQueue
- * @see java.util.concurrent.BlockingQueue
- * @see java.util.concurrent.ArrayBlockingQueue
- * @see java.util.concurrent.LinkedBlockingQueue
- * @see java.util.concurrent.PriorityBlockingQueue
  * @since 1.5
  * @author Doug Lea
  * @param <E> the type of elements held in this queue
diff --git a/jdk/src/java.base/share/classes/java/util/Vector.java b/jdk/src/java.base/share/classes/java/util/Vector.java
index 6e833f7dd6a..f6cdfbe8e9c 100644
--- a/jdk/src/java.base/share/classes/java/util/Vector.java
+++ b/jdk/src/java.base/share/classes/java/util/Vector.java
@@ -233,42 +233,56 @@ public class Vector<E>
     public synchronized void ensureCapacity(int minCapacity) {
         if (minCapacity > 0) {
             modCount++;
-            ensureCapacityHelper(minCapacity);
+            if (minCapacity > elementData.length)
+                grow(minCapacity);
         }
     }
 
     /**
-     * This implements the unsynchronized semantics of ensureCapacity.
-     * Synchronized methods in this class can internally call this
-     * method for ensuring capacity without incurring the cost of an
-     * extra synchronization.
-     *
-     * @see #ensureCapacity(int)
-     */
-    private void ensureCapacityHelper(int minCapacity) {
-        // overflow-conscious code
-        if (minCapacity - elementData.length > 0)
-            grow(minCapacity);
-    }
-
-    /**
-     * The maximum size of array to allocate.
+     * The maximum size of array to allocate (unless necessary).
      * Some VMs reserve some header words in an array.
      * Attempts to allocate larger arrays may result in
      * OutOfMemoryError: Requested array size exceeds VM limit
      */
     private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
 
-    private void grow(int minCapacity) {
+    /**
+     * Increases the capacity to ensure that it can hold at least the
+     * number of elements specified by the minimum capacity argument.
+     *
+     * @param minCapacity the desired minimum capacity
+     * @throws OutOfMemoryError if minCapacity is less than zero
+     */
+    private Object[] grow(int minCapacity) {
+        return elementData = Arrays.copyOf(elementData,
+                                           newCapacity(minCapacity));
+    }
+
+    private Object[] grow() {
+        return grow(elementCount + 1);
+    }
+
+    /**
+     * Returns a capacity at least as large as the given minimum capacity.
+     * Will not return a capacity greater than MAX_ARRAY_SIZE unless
+     * the given minimum capacity is greater than MAX_ARRAY_SIZE.
+     *
+     * @param minCapacity the desired minimum capacity
+     * @throws OutOfMemoryError if minCapacity is less than zero
+     */
+    private int newCapacity(int minCapacity) {
         // overflow-conscious code
         int oldCapacity = elementData.length;
         int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                          capacityIncrement : oldCapacity);
-        if (newCapacity - minCapacity < 0)
-            newCapacity = minCapacity;
-        if (newCapacity - MAX_ARRAY_SIZE > 0)
-            newCapacity = hugeCapacity(minCapacity);
-        elementData = Arrays.copyOf(elementData, newCapacity);
+        if (newCapacity - minCapacity <= 0) {
+            if (minCapacity < 0) // overflow
+                throw new OutOfMemoryError();
+            return minCapacity;
+        }
+        return (newCapacity - MAX_ARRAY_SIZE <= 0)
+            ? newCapacity
+            : hugeCapacity(minCapacity);
     }
 
     private static int hugeCapacity(int minCapacity) {
@@ -290,13 +304,10 @@ public class Vector<E>
      */
     public synchronized void setSize(int newSize) {
         modCount++;
-        if (newSize > elementCount) {
-            ensureCapacityHelper(newSize);
-        } else {
-            for (int i = newSize ; i < elementCount ; i++) {
-                elementData[i] = null;
-            }
-        }
+        if (newSize > elementData.length)
+            grow(newSize);
+        for (int i = newSize; i < elementCount; i++)
+            elementData[i] = null;
         elementCount = newSize;
     }
 
@@ -604,11 +615,16 @@ public class Vector<E>
             throw new ArrayIndexOutOfBoundsException(index
                                                      + " > " + elementCount);
         }
-        ensureCapacityHelper(elementCount + 1);
-        System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
-        elementData[index] = obj;
         modCount++;
-        elementCount++;
+        final int s = elementCount;
+        Object[] elementData = this.elementData;
+        if (s == elementData.length)
+            elementData = grow();
+        System.arraycopy(elementData, index,
+                         elementData, index + 1,
+                         s - index);
+        elementData[index] = obj;
+        elementCount = s + 1;
     }
 
     /**
@@ -623,9 +639,8 @@ public class Vector<E>
      * @param   obj   the component to be added
      */
     public synchronized void addElement(E obj) {
-        ensureCapacityHelper(elementCount + 1);
         modCount++;
-        elementData[elementCount++] = obj;
+        add(obj, elementData, elementCount);
     }
 
     /**
@@ -780,6 +795,18 @@ public class Vector<E>
         return oldValue;
     }
 
+    /**
+     * This helper method split out from add(E) to keep method
+     * bytecode size under 35 (the -XX:MaxInlineSize default value),
+     * which helps when add(E) is called in a C1-compiled loop.
+     */
+    private void add(E e, Object[] elementData, int s) {
+        if (s == elementData.length)
+            elementData = grow();
+        elementData[s] = e;
+        elementCount = s + 1;
+    }
+
     /**
      * Appends the specified element to the end of this Vector.
      *
@@ -788,9 +815,8 @@ public class Vector<E>
      * @since 1.2
      */
     public synchronized boolean add(E e) {
-        ensureCapacityHelper(elementCount + 1);
         modCount++;
-        elementData[elementCount++] = e;
+        add(e, elementData, elementCount);
         return true;
     }
 
@@ -891,16 +917,19 @@ public class Vector<E>
      */
     public boolean addAll(Collection<? extends E> c) {
         Object[] a = c.toArray();
+        modCount++;
         int numNew = a.length;
-        if (numNew > 0) {
-            synchronized (this) {
-                ensureCapacityHelper(elementCount + numNew);
-                System.arraycopy(a, 0, elementData, elementCount, numNew);
-                modCount++;
-                elementCount += numNew;
-            }
+        if (numNew == 0)
+            return false;
+        synchronized (this) {
+            Object[] elementData = this.elementData;
+            final int s = elementCount;
+            if (numNew > elementData.length - s)
+                elementData = grow(s + numNew);
+            System.arraycopy(a, 0, elementData, s, numNew);
+            elementCount = s + numNew;
+            return true;
         }
-        return numNew > 0;
     }
 
     /**
@@ -969,21 +998,23 @@ public class Vector<E>
             throw new ArrayIndexOutOfBoundsException(index);
 
         Object[] a = c.toArray();
+        modCount++;
         int numNew = a.length;
+        if (numNew == 0)
+            return false;
+        Object[] elementData = this.elementData;
+        final int s = elementCount;
+        if (numNew > elementData.length - s)
+            elementData = grow(s + numNew);
 
-        if (numNew > 0) {
-            ensureCapacityHelper(elementCount + numNew);
-
-            int numMoved = elementCount - index;
-            if (numMoved > 0)
-                System.arraycopy(elementData, index, elementData,
-                        index + numNew, numMoved);
-
-             System.arraycopy(a, 0, elementData, index, numNew);
-             elementCount += numNew;
-             modCount++;
-        }
-        return numNew > 0;
+        int numMoved = s - index;
+        if (numMoved > 0)
+            System.arraycopy(elementData, index,
+                             elementData, index + numNew,
+                             numMoved);
+        System.arraycopy(a, 0, elementData, index, numNew);
+        elementCount = s + numNew;
+        return true;
     }
 
     /**
diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentMap.java b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentMap.java
index 37b97c36265..d301de9eee6 100644
--- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentMap.java
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentMap.java
@@ -301,19 +301,15 @@ public interface ConcurrentMap<K,V> extends Map<K,V> {
      *
      * @implSpec
      * The default implementation is equivalent to the following steps for this
-     * {@code map}, then returning the current value or {@code null} if now
-     * absent:
+     * {@code map}:
      *
      * <pre> {@code
-     * if (map.get(key) == null) {
-     *   V newValue = mappingFunction.apply(key);
-     *   if (newValue != null)
-     *     return map.putIfAbsent(key, newValue);
-     * }}</pre>
-     *
-     * The default implementation may retry these steps when multiple
-     * threads attempt updates including potentially calling the mapping
-     * function multiple times.
+     * V oldValue, newValue;
+     * return ((oldValue = map.get(key)) == null
+     *         && (newValue = mappingFunction.apply(key)) != null
+     *         && (oldValue = map.putIfAbsent(key, newValue)) == null)
+     *   ? newValue
+     *   : oldValue;}</pre>
      *
      * <p>This implementation assumes that the ConcurrentMap cannot contain null
      * values and {@code get()} returning null unambiguously means the key is
@@ -323,16 +319,19 @@ public interface ConcurrentMap<K,V> extends Map<K,V> {
      * @throws UnsupportedOperationException {@inheritDoc}
      * @throws ClassCastException {@inheritDoc}
      * @throws NullPointerException {@inheritDoc}
+     * @throws IllegalArgumentException {@inheritDoc}
      * @since 1.8
      */
     @Override
     default V computeIfAbsent(K key,
             Function<? super K, ? extends V> mappingFunction) {
         Objects.requireNonNull(mappingFunction);
-        V v, newValue;
-        return ((v = get(key)) == null &&
-                (newValue = mappingFunction.apply(key)) != null &&
-                (v = putIfAbsent(key, newValue)) == null) ? newValue : v;
+        V oldValue, newValue;
+        return ((oldValue = get(key)) == null
+                && (newValue = mappingFunction.apply(key)) != null
+                && (oldValue = putIfAbsent(key, newValue)) == null)
+            ? newValue
+            : oldValue;
     }
 
     /**
@@ -340,22 +339,19 @@ public interface ConcurrentMap<K,V> extends Map<K,V> {
      *
      * @implSpec
      * The default implementation is equivalent to performing the following
-     * steps for this {@code map}, then returning the current value or
-     * {@code null} if now absent:
+     * steps for this {@code map}:
      *
      * <pre> {@code
-     * if (map.get(key) != null) {
-     *   V oldValue = map.get(key);
+     * for (V oldValue; (oldValue = map.get(key)) != null; ) {
      *   V newValue = remappingFunction.apply(key, oldValue);
-     *   if (newValue != null)
-     *     map.replace(key, oldValue, newValue);
-     *   else
-     *     map.remove(key, oldValue);
-     * }}</pre>
-     *
-     * The default implementation may retry these steps when multiple threads
-     * attempt updates including potentially calling the remapping function
-     * multiple times.
+     *   if ((newValue == null)
+     *       ? map.remove(key, oldValue)
+     *       : map.replace(key, oldValue, newValue))
+     *     return newValue;
+     * }
+     * return null;}</pre>
+     * When multiple threads attempt updates, map operations and the
+     * remapping function may be called multiple times.
      *
      * <p>This implementation assumes that the ConcurrentMap cannot contain null
      * values and {@code get()} returning null unambiguously means the key is
@@ -365,22 +361,21 @@ public interface ConcurrentMap<K,V> extends Map<K,V> {
      * @throws UnsupportedOperationException {@inheritDoc}
      * @throws ClassCastException {@inheritDoc}
      * @throws NullPointerException {@inheritDoc}
+     * @throws IllegalArgumentException {@inheritDoc}
      * @since 1.8
      */
     @Override
     default V computeIfPresent(K key,
             BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
         Objects.requireNonNull(remappingFunction);
-        V oldValue;
-        while ((oldValue = get(key)) != null) {
+        for (V oldValue; (oldValue = get(key)) != null; ) {
             V newValue = remappingFunction.apply(key, oldValue);
-            if (newValue != null) {
-                if (replace(key, oldValue, newValue))
-                    return newValue;
-            } else if (remove(key, oldValue))
-                return null;
+            if ((newValue == null)
+                ? remove(key, oldValue)
+                : replace(key, oldValue, newValue))
+                return newValue;
         }
-        return oldValue;
+        return null;
     }
 
     /**
@@ -388,27 +383,23 @@ public interface ConcurrentMap<K,V> extends Map<K,V> {
      *
      * @implSpec
      * The default implementation is equivalent to performing the following
-     * steps for this {@code map}, then returning the current value or
-     * {@code null} if absent:
+     * steps for this {@code map}:
      *
      * <pre> {@code
-     * V oldValue = map.get(key);
-     * V newValue = remappingFunction.apply(key, oldValue);
-     * if (oldValue != null ) {
-     *   if (newValue != null)
-     *     map.replace(key, oldValue, newValue);
-     *   else
-     *     map.remove(key, oldValue);
-     * } else {
-     *   if (newValue != null)
-     *     map.putIfAbsent(key, newValue);
-     *   else
+     * for (;;) {
+     *   V oldValue = map.get(key);
+     *   V newValue = remappingFunction.apply(key, oldValue);
+     *   if (newValue != null) {
+     *     if ((oldValue != null)
+     *       ? map.replace(key, oldValue, newValue)
+     *       : map.putIfAbsent(key, newValue) == null)
+     *       return newValue;
+     *   } else if (oldValue == null || map.remove(key, oldValue)) {
      *     return null;
+     *   }
      * }}</pre>
-     *
-     * The default implementation may retry these steps when multiple
-     * threads attempt updates including potentially calling the remapping
-     * function multiple times.
+     * When multiple threads attempt updates, map operations and the
+     * remapping function may be called multiple times.
      *
      * <p>This implementation assumes that the ConcurrentMap cannot contain null
      * values and {@code get()} returning null unambiguously means the key is
@@ -418,50 +409,29 @@ public interface ConcurrentMap<K,V> extends Map<K,V> {
      * @throws UnsupportedOperationException {@inheritDoc}
      * @throws ClassCastException {@inheritDoc}
      * @throws NullPointerException {@inheritDoc}
+     * @throws IllegalArgumentException {@inheritDoc}
      * @since 1.8
      */
     @Override
     default V compute(K key,
-            BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
-        Objects.requireNonNull(remappingFunction);
-        V oldValue = get(key);
-        for (;;) {
-            V newValue = remappingFunction.apply(key, oldValue);
-            if (newValue == null) {
-                // delete mapping
-                if (oldValue != null || containsKey(key)) {
-                    // something to remove
-                    if (remove(key, oldValue)) {
-                        // removed the old value as expected
-                        return null;
+                      BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+        retry: for (;;) {
+            V oldValue = get(key);
+            // if putIfAbsent fails, opportunistically use its return value
+            haveOldValue: for (;;) {
+                V newValue = remappingFunction.apply(key, oldValue);
+                if (newValue != null) {
+                    if (oldValue != null) {
+                        if (replace(key, oldValue, newValue))
+                            return newValue;
                     }
-
-                    // some other value replaced old value. try again.
-                    oldValue = get(key);
-                } else {
-                    // nothing to do. Leave things as they were.
+                    else if ((oldValue = putIfAbsent(key, newValue)) == null)
+                        return newValue;
+                    else continue haveOldValue;
+                } else if (oldValue == null || remove(key, oldValue)) {
                     return null;
                 }
-            } else {
-                // add or replace old mapping
-                if (oldValue != null) {
-                    // replace
-                    if (replace(key, oldValue, newValue)) {
-                        // replaced as expected.
-                        return newValue;
-                    }
-
-                    // some other value replaced old value. try again.
-                    oldValue = get(key);
-                } else {
-                    // add (replace if oldValue was null)
-                    if ((oldValue = putIfAbsent(key, newValue)) == null) {
-                        // replaced
-                        return newValue;
-                    }
-
-                    // some other value replaced old value. try again.
-                }
+                continue retry;
             }
         }
     }
@@ -471,21 +441,25 @@ public interface ConcurrentMap<K,V> extends Map<K,V> {
      *
      * @implSpec
      * The default implementation is equivalent to performing the following
-     * steps for this {@code map}, then returning the current value or
-     * {@code null} if absent:
+     * steps for this {@code map}:
      *
      * <pre> {@code
-     * V oldValue = map.get(key);
-     * V newValue = (oldValue == null) ? value :
-     *     remappingFunction.apply(oldValue, value);
-     * if (newValue == null)
-     *   map.remove(key);
-     * else
-     *   map.put(key, newValue);}</pre>
-     *
-     * <p>The default implementation may retry these steps when multiple
-     * threads attempt updates including potentially calling the remapping
-     * function multiple times.
+     * for (;;) {
+     *   V oldValue = map.get(key);
+     *   if (oldValue != null) {
+     *     V newValue = remappingFunction.apply(oldValue, value);
+     *     if (newValue != null) {
+     *       if (map.replace(key, oldValue, newValue))
+     *         return newValue;
+     *     } else if (map.remove(key, oldValue)) {
+     *       return null;
+     *     }
+     *   } else if (map.putIfAbsent(key, value) == null) {
+     *     return value;
+     *   }
+     * }}</pre>
+     * When multiple threads attempt updates, map operations and the
+     * remapping function may be called multiple times.
      *
      * <p>This implementation assumes that the ConcurrentMap cannot contain null
      * values and {@code get()} returning null unambiguously means the key is
@@ -495,6 +469,7 @@ public interface ConcurrentMap<K,V> extends Map<K,V> {
      * @throws UnsupportedOperationException {@inheritDoc}
      * @throws ClassCastException {@inheritDoc}
      * @throws NullPointerException {@inheritDoc}
+     * @throws IllegalArgumentException {@inheritDoc}
      * @since 1.8
      */
     @Override
@@ -502,20 +477,23 @@ public interface ConcurrentMap<K,V> extends Map<K,V> {
             BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
         Objects.requireNonNull(remappingFunction);
         Objects.requireNonNull(value);
-        V oldValue = get(key);
-        for (;;) {
-            if (oldValue != null) {
-                V newValue = remappingFunction.apply(oldValue, value);
-                if (newValue != null) {
-                    if (replace(key, oldValue, newValue))
-                        return newValue;
-                } else if (remove(key, oldValue)) {
-                    return null;
-                }
-                oldValue = get(key);
-            } else {
-                if ((oldValue = putIfAbsent(key, value)) == null) {
-                    return value;
+        retry: for (;;) {
+            V oldValue = get(key);
+            // if putIfAbsent fails, opportunistically use its return value
+            haveOldValue: for (;;) {
+                if (oldValue != null) {
+                    V newValue = remappingFunction.apply(oldValue, value);
+                    if (newValue != null) {
+                        if (replace(key, oldValue, newValue))
+                            return newValue;
+                    } else if (remove(key, oldValue)) {
+                        return null;
+                    }
+                    continue retry;
+                } else {
+                    if ((oldValue = putIfAbsent(key, value)) == null)
+                        return value;
+                    continue haveOldValue;
                 }
             }
         }
diff --git a/jdk/src/java.base/share/classes/sun/nio/ch/Util.java b/jdk/src/java.base/share/classes/sun/nio/ch/Util.java
index 3b8d814bcd1..f2095540180 100644
--- a/jdk/src/java.base/share/classes/sun/nio/ch/Util.java
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/Util.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2016, 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
@@ -44,6 +44,9 @@ public class Util {
     // The number of temp buffers in our pool
     private static final int TEMP_BUF_POOL_SIZE = IOUtil.IOV_MAX;
 
+    // The max size allowed for a cached temp buffer, in bytes
+    private static final long MAX_CACHED_BUFFER_SIZE = getMaxCachedBufferSize();
+
     // Per-thread cache of temporary direct buffers
     private static ThreadLocal<BufferCache> bufferCache =
         new ThreadLocal<BufferCache>()
@@ -54,6 +57,52 @@ public class Util {
         }
     };
 
+    /**
+     * Returns the max size allowed for a cached temp buffers, in
+     * bytes. It defaults to Long.MAX_VALUE. It can be set with the
+     * jdk.nio.maxCachedBufferSize property. Even though
+     * ByteBuffer.capacity() returns an int, we're using a long here
+     * for potential future-proofing.
+     */
+    private static long getMaxCachedBufferSize() {
+        String s = java.security.AccessController.doPrivileged(
+            new PrivilegedAction<String>() {
+                @Override
+                public String run() {
+                    return System.getProperty("jdk.nio.maxCachedBufferSize");
+                }
+            });
+        if (s != null) {
+            try {
+                long m = Long.parseLong(s);
+                if (m >= 0) {
+                    return m;
+                } else {
+                    // if it's negative, ignore the system property
+                }
+            } catch (NumberFormatException e) {
+                // if the string is not well formed, ignore the system property
+            }
+        }
+        return Long.MAX_VALUE;
+    }
+
+    /**
+     * Returns true if a buffer of this size is too large to be
+     * added to the buffer cache, false otherwise.
+     */
+    private static boolean isBufferTooLarge(int size) {
+        return size > MAX_CACHED_BUFFER_SIZE;
+    }
+
+    /**
+     * Returns true if the buffer is too large to be added to the
+     * buffer cache, false otherwise.
+     */
+    private static boolean isBufferTooLarge(ByteBuffer buf) {
+        return isBufferTooLarge(buf.capacity());
+    }
+
     /**
      * A simple cache of direct buffers.
      */
@@ -80,6 +129,9 @@ public class Util {
          * size (or null if no suitable buffer is found).
          */
         ByteBuffer get(int size) {
+            // Don't call this if the buffer would be too large.
+            assert !isBufferTooLarge(size);
+
             if (count == 0)
                 return null;  // cache is empty
 
@@ -117,6 +169,9 @@ public class Util {
         }
 
         boolean offerFirst(ByteBuffer buf) {
+            // Don't call this if the buffer is too large.
+            assert !isBufferTooLarge(buf);
+
             if (count >= TEMP_BUF_POOL_SIZE) {
                 return false;
             } else {
@@ -128,6 +183,9 @@ public class Util {
         }
 
         boolean offerLast(ByteBuffer buf) {
+            // Don't call this if the buffer is too large.
+            assert !isBufferTooLarge(buf);
+
             if (count >= TEMP_BUF_POOL_SIZE) {
                 return false;
             } else {
@@ -156,6 +214,15 @@ public class Util {
      * Returns a temporary buffer of at least the given size
      */
     public static ByteBuffer getTemporaryDirectBuffer(int size) {
+        // If a buffer of this size is too large for the cache, there
+        // should not be a buffer in the cache that is at least as
+        // large. So we'll just create a new one. Also, we don't have
+        // to remove the buffer from the cache (as this method does
+        // below) given that we won't put the new buffer in the cache.
+        if (isBufferTooLarge(size)) {
+            return ByteBuffer.allocateDirect(size);
+        }
+
         BufferCache cache = bufferCache.get();
         ByteBuffer buf = cache.get(size);
         if (buf != null) {
@@ -185,6 +252,13 @@ public class Util {
      * likely to be returned by a subsequent call to getTemporaryDirectBuffer.
      */
     static void offerFirstTemporaryDirectBuffer(ByteBuffer buf) {
+        // If the buffer is too large for the cache we don't have to
+        // check the cache. We'll just free it.
+        if (isBufferTooLarge(buf)) {
+            free(buf);
+            return;
+        }
+
         assert buf != null;
         BufferCache cache = bufferCache.get();
         if (!cache.offerFirst(buf)) {
@@ -200,6 +274,13 @@ public class Util {
      * cache in same order that they were obtained.
      */
     static void offerLastTemporaryDirectBuffer(ByteBuffer buf) {
+        // If the buffer is too large for the cache we don't have to
+        // check the cache. We'll just free it.
+        if (isBufferTooLarge(buf)) {
+            free(buf);
+            return;
+        }
+
         assert buf != null;
         BufferCache cache = bufferCache.get();
         if (!cache.offerLast(buf)) {
diff --git a/jdk/src/java.base/share/classes/sun/nio/fs/AbstractPoller.java b/jdk/src/java.base/share/classes/sun/nio/fs/AbstractPoller.java
index 84fbae5dddd..fa2d9a90991 100644
--- a/jdk/src/java.base/share/classes/sun/nio/fs/AbstractPoller.java
+++ b/jdk/src/java.base/share/classes/sun/nio/fs/AbstractPoller.java
@@ -59,7 +59,11 @@ abstract class AbstractPoller implements Runnable {
         AccessController.doPrivileged(new PrivilegedAction<>() {
             @Override
             public Object run() {
-                Thread thr = new Thread(null, thisRunnable, "FileSystemWatchService", 0, false);
+                Thread thr = new Thread(null,
+                                        thisRunnable,
+                                        "FileSystemWatchService",
+                                        0,
+                                        false);
                 thr.setDaemon(true);
                 thr.start();
                 return null;
@@ -216,10 +220,10 @@ abstract class AbstractPoller implements Runnable {
                 throw new ClosedWatchServiceException();
             }
             requestList.add(req);
-        }
 
-        // wakeup thread
-        wakeup();
+            // wakeup thread
+            wakeup();
+        }
 
         // wait for result
         Object result = req.awaitResult();
@@ -244,6 +248,7 @@ abstract class AbstractPoller implements Runnable {
                 // if in process of shutdown then reject request
                 if (shutdown) {
                     req.release(new ClosedWatchServiceException());
+                    continue;
                 }
 
                 switch (req.type()) {
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/AbstractOptionSpec.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/AbstractOptionSpec.java
new file mode 100644
index 00000000000..0d39af447f7
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/AbstractOptionSpec.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import static java.util.Collections.*;
+
+import jdk.internal.joptsimple.internal.Reflection;
+import jdk.internal.joptsimple.internal.ReflectionException;
+
+import static jdk.internal.joptsimple.internal.Strings.*;
+
+/**
+ * @param <V> represents the type of the arguments this option accepts
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+abstract class AbstractOptionSpec<V> implements OptionSpec<V>, OptionDescriptor {
+    private final List<String> options = new ArrayList<String>();
+    private final String description;
+    private boolean forHelp;
+
+    protected AbstractOptionSpec( String option ) {
+        this( singletonList( option ), EMPTY );
+    }
+
+    protected AbstractOptionSpec( Collection<String> options, String description ) {
+        arrangeOptions( options );
+
+        this.description = description;
+    }
+
+    public final Collection<String> options() {
+        return unmodifiableList( options );
+    }
+
+    public final List<V> values( OptionSet detectedOptions ) {
+        return detectedOptions.valuesOf( this );
+    }
+
+    public final V value( OptionSet detectedOptions ) {
+        return detectedOptions.valueOf( this );
+    }
+
+    public String description() {
+        return description;
+    }
+
+    public final AbstractOptionSpec<V> forHelp() {
+        forHelp = true;
+        return this;
+    }
+
+    public final boolean isForHelp() {
+        return forHelp;
+    }
+
+    public boolean representsNonOptions() {
+        return false;
+    }
+
+    protected abstract V convert( String argument );
+
+    protected V convertWith( ValueConverter<V> converter, String argument ) {
+        try {
+            return Reflection.convertWith( converter, argument );
+        }
+        catch ( ReflectionException ex ) {
+            throw new OptionArgumentConversionException( options(), argument, ex );
+        }
+        catch ( ValueConversionException ex ) {
+            throw new OptionArgumentConversionException( options(), argument, ex );
+        }
+    }
+
+    protected String argumentTypeIndicatorFrom( ValueConverter<V> converter ) {
+        if ( converter == null )
+            return null;
+
+        String pattern = converter.valuePattern();
+        return pattern == null ? converter.valueType().getName() : pattern;
+    }
+
+    abstract void handleOption( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions,
+        String detectedArgument );
+
+    private void arrangeOptions( Collection<String> unarranged ) {
+        if ( unarranged.size() == 1 ) {
+            options.addAll( unarranged );
+            return;
+        }
+
+        List<String> shortOptions = new ArrayList<String>();
+        List<String> longOptions = new ArrayList<String>();
+
+        for ( String each : unarranged ) {
+            if ( each.length() == 1 )
+                shortOptions.add( each );
+            else
+                longOptions.add( each );
+        }
+
+        sort( shortOptions );
+        sort( longOptions );
+
+        options.addAll( shortOptions );
+        options.addAll( longOptions );
+    }
+
+    @Override
+    public boolean equals( Object that ) {
+        if ( !( that instanceof AbstractOptionSpec<?> ) )
+            return false;
+
+        AbstractOptionSpec<?> other = (AbstractOptionSpec<?>) that;
+        return options.equals( other.options );
+    }
+
+    @Override
+    public int hashCode() {
+        return options.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return options.toString();
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/AlternativeLongOptionSpec.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/AlternativeLongOptionSpec.java
new file mode 100644
index 00000000000..12532c6b3bc
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/AlternativeLongOptionSpec.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple;
+
+import static java.util.Collections.*;
+
+import static jdk.internal.joptsimple.ParserRules.*;
+
+/**
+ * Represents the <kbd>"-W"</kbd> form of long option specification.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class AlternativeLongOptionSpec extends ArgumentAcceptingOptionSpec<String> {
+    AlternativeLongOptionSpec() {
+        super( singletonList( RESERVED_FOR_EXTENSIONS ), true, "Alternative form of long options" );
+
+        describedAs( "opt=value" );
+    }
+
+    @Override
+    protected void detectOptionArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions ) {
+        if ( !arguments.hasMore() )
+            throw new OptionMissingRequiredArgumentException( options() );
+
+        arguments.treatNextAsLongOption();
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ArgumentAcceptingOptionSpec.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ArgumentAcceptingOptionSpec.java
new file mode 100644
index 00000000000..ffb083746e9
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ArgumentAcceptingOptionSpec.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import static java.util.Collections.*;
+
+import static jdk.internal.joptsimple.internal.Objects.*;
+import static jdk.internal.joptsimple.internal.Reflection.*;
+import static jdk.internal.joptsimple.internal.Strings.*;
+
+/**
+ * <p>Specification of an option that accepts an argument.</p>
+ *
+ * <p>Instances are returned from {@link OptionSpecBuilder} methods to allow the formation of parser directives as
+ * sentences in a "fluent interface" language. For example:</p>
+ *
+ * <pre>
+ *   <code>
+ *   OptionParser parser = new OptionParser();
+ *   parser.accepts( "c" ).withRequiredArg().<strong>ofType( Integer.class )</strong>;
+ *   </code>
+ * </pre>
+ *
+ * <p>If no methods are invoked on an instance of this class, then that instance's option will treat its argument as
+ * a {@link String}.</p>
+ *
+ * @param <V> represents the type of the arguments this option accepts
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public abstract class ArgumentAcceptingOptionSpec<V> extends AbstractOptionSpec<V> {
+    private static final char NIL_VALUE_SEPARATOR = '\u0000';
+
+    private boolean optionRequired;
+    private final boolean argumentRequired;
+    private ValueConverter<V> converter;
+    private String argumentDescription = "";
+    private String valueSeparator = String.valueOf( NIL_VALUE_SEPARATOR );
+    private final List<V> defaultValues = new ArrayList<V>();
+
+    ArgumentAcceptingOptionSpec( String option, boolean argumentRequired ) {
+        super( option );
+
+        this.argumentRequired = argumentRequired;
+    }
+
+    ArgumentAcceptingOptionSpec( Collection<String> options, boolean argumentRequired, String description ) {
+        super( options, description );
+
+        this.argumentRequired = argumentRequired;
+    }
+
+    /**
+     * <p>Specifies a type to which arguments of this spec's option are to be converted.</p>
+     *
+     * <p>JOpt Simple accepts types that have either:</p>
+     *
+     * <ol>
+     *   <li>a public static method called {@code valueOf} which accepts a single argument of type {@link String}
+     *   and whose return type is the same as the class on which the method is declared.  The {@code java.lang}
+     *   primitive wrapper classes have such methods.</li>
+     *
+     *   <li>a public constructor which accepts a single argument of type {@link String}.</li>
+     * </ol>
+     *
+     * <p>This class converts arguments using those methods in that order; that is, {@code valueOf} would be invoked
+     * before a one-{@link String}-arg constructor would.</p>
+     *
+     * <p>Invoking this method will trump any previous calls to this method or to
+     * {@link #withValuesConvertedBy(ValueConverter)}.</p>
+     *
+     * @param <T> represents the runtime class of the desired option argument type
+     * @param argumentType desired type of arguments to this spec's option
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     * @throws NullPointerException if the type is {@code null}
+     * @throws IllegalArgumentException if the type does not have the standard conversion methods
+     */
+    public final <T> ArgumentAcceptingOptionSpec<T> ofType( Class<T> argumentType ) {
+        return withValuesConvertedBy( findConverter( argumentType ) );
+    }
+
+    /**
+     * <p>Specifies a converter to use to translate arguments of this spec's option into Java objects.  This is useful
+     * when converting to types that do not have the requisite factory method or constructor for
+     * {@link #ofType(Class)}.</p>
+     *
+     * <p>Invoking this method will trump any previous calls to this method or to {@link #ofType(Class)}.
+     *
+     * @param <T> represents the runtime class of the desired option argument type
+     * @param aConverter the converter to use
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     * @throws NullPointerException if the converter is {@code null}
+     */
+    @SuppressWarnings( "unchecked" )
+    public final <T> ArgumentAcceptingOptionSpec<T> withValuesConvertedBy( ValueConverter<T> aConverter ) {
+        if ( aConverter == null )
+            throw new NullPointerException( "illegal null converter" );
+
+        converter = (ValueConverter<V>) aConverter;
+        return (ArgumentAcceptingOptionSpec<T>) this;
+    }
+
+    /**
+     * <p>Specifies a description for the argument of the option that this spec represents.  This description is used
+     * when generating help information about the parser.</p>
+     *
+     * @param description describes the nature of the argument of this spec's option
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     */
+    public final ArgumentAcceptingOptionSpec<V> describedAs( String description ) {
+        argumentDescription = description;
+        return this;
+    }
+
+    /**
+     * <p>Specifies a value separator for the argument of the option that this spec represents.  This allows a single
+     * option argument to represent multiple values for the option.  For example:</p>
+     *
+     * <pre>
+     *   <code>
+     *   parser.accepts( "z" ).withRequiredArg()
+     *       .<strong>withValuesSeparatedBy( ',' )</strong>;
+     *   OptionSet options = parser.parse( new String[] { "-z", "foo,bar,baz", "-z",
+     *       "fizz", "-z", "buzz" } );
+     *   </code>
+     * </pre>
+     *
+     * <p>Then {@code options.valuesOf( "z" )} would yield the list {@code [foo, bar, baz, fizz, buzz]}.</p>
+     *
+     * <p>You cannot use Unicode U+0000 as the separator.</p>
+     *
+     * @param separator a character separator
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     * @throws IllegalArgumentException if the separator is Unicode U+0000
+     */
+    public final ArgumentAcceptingOptionSpec<V> withValuesSeparatedBy( char separator ) {
+        if ( separator == NIL_VALUE_SEPARATOR )
+            throw new IllegalArgumentException( "cannot use U+0000 as separator" );
+
+        valueSeparator = String.valueOf( separator );
+        return this;
+    }
+
+    /**
+     * <p>Specifies a value separator for the argument of the option that this spec represents.  This allows a single
+     * option argument to represent multiple values for the option.  For example:</p>
+     *
+     * <pre>
+     *   <code>
+     *   parser.accepts( "z" ).withRequiredArg()
+     *       .<strong>withValuesSeparatedBy( ":::" )</strong>;
+     *   OptionSet options = parser.parse( new String[] { "-z", "foo:::bar:::baz", "-z",
+     *       "fizz", "-z", "buzz" } );
+     *   </code>
+     * </pre>
+     *
+     * <p>Then {@code options.valuesOf( "z" )} would yield the list {@code [foo, bar, baz, fizz, buzz]}.</p>
+     *
+     * <p>You cannot use Unicode U+0000 in the separator.</p>
+     *
+     * @param separator a string separator
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     * @throws IllegalArgumentException if the separator contains Unicode U+0000
+     */
+    public final ArgumentAcceptingOptionSpec<V> withValuesSeparatedBy( String separator ) {
+        if ( separator.indexOf( NIL_VALUE_SEPARATOR ) != -1 )
+            throw new IllegalArgumentException( "cannot use U+0000 in separator" );
+
+        valueSeparator = separator;
+        return this;
+    }
+
+    /**
+     * Specifies a set of default values for the argument of the option that this spec represents.
+     *
+     * @param value the first in the set of default argument values for this spec's option
+     * @param values the (optional) remainder of the set of default argument values for this spec's option
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     * @throws NullPointerException if {@code value}, {@code values}, or any elements of {@code values} are
+     * {@code null}
+     */
+    @SuppressWarnings("unchecked")
+    public ArgumentAcceptingOptionSpec<V> defaultsTo( V value, V... values ) {
+        addDefaultValue( value );
+        defaultsTo( values );
+
+        return this;
+    }
+
+    /**
+     * Specifies a set of default values for the argument of the option that this spec represents.
+     *
+     * @param values the set of default argument values for this spec's option
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     * @throws NullPointerException if {@code values} or any elements of {@code values} are {@code null}
+     */
+    public ArgumentAcceptingOptionSpec<V> defaultsTo( V[] values ) {
+        for ( V each : values )
+            addDefaultValue( each );
+
+        return this;
+    }
+
+    /**
+     * Marks this option as required. An {@link OptionException} will be thrown when
+     * {@link OptionParser#parse(java.lang.String...)} is called, if an option is marked as required and not specified
+     * on the command line.
+     *
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     */
+    public ArgumentAcceptingOptionSpec<V> required() {
+        optionRequired = true;
+        return this;
+    }
+
+    public boolean isRequired() {
+        return optionRequired;
+    }
+
+    private void addDefaultValue( V value ) {
+        ensureNotNull( value );
+        defaultValues.add( value );
+    }
+
+    @Override
+    final void handleOption( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions,
+        String detectedArgument ) {
+
+        if ( isNullOrEmpty( detectedArgument ) )
+            detectOptionArgument( parser, arguments, detectedOptions );
+        else
+            addArguments( detectedOptions, detectedArgument );
+    }
+
+    protected void addArguments( OptionSet detectedOptions, String detectedArgument ) {
+        StringTokenizer lexer = new StringTokenizer( detectedArgument, valueSeparator );
+        if ( !lexer.hasMoreTokens() )
+            detectedOptions.addWithArgument( this, detectedArgument );
+        else {
+            while ( lexer.hasMoreTokens() )
+                detectedOptions.addWithArgument( this, lexer.nextToken() );
+        }
+    }
+
+    protected abstract void detectOptionArgument( OptionParser parser, ArgumentList arguments,
+        OptionSet detectedOptions );
+
+    @Override
+    protected final V convert( String argument ) {
+        return convertWith( converter, argument );
+    }
+
+    protected boolean canConvertArgument( String argument ) {
+        StringTokenizer lexer = new StringTokenizer( argument, valueSeparator );
+
+        try {
+            while ( lexer.hasMoreTokens() )
+                convert( lexer.nextToken() );
+            return true;
+        }
+        catch ( OptionException ignored ) {
+            return false;
+        }
+    }
+
+    protected boolean isArgumentOfNumberType() {
+        return converter != null && Number.class.isAssignableFrom( converter.valueType() );
+    }
+
+    public boolean acceptsArguments() {
+        return true;
+    }
+
+    public boolean requiresArgument() {
+        return argumentRequired;
+    }
+
+    public String argumentDescription() {
+        return argumentDescription;
+    }
+
+    public String argumentTypeIndicator() {
+        return argumentTypeIndicatorFrom( converter );
+    }
+
+    public List<V> defaultValues() {
+        return unmodifiableList( defaultValues );
+    }
+
+    @Override
+    public boolean equals( Object that ) {
+        if ( !super.equals( that ) )
+            return false;
+
+        ArgumentAcceptingOptionSpec<?> other = (ArgumentAcceptingOptionSpec<?>) that;
+        return requiresArgument() == other.requiresArgument();
+    }
+
+    @Override
+    public int hashCode() {
+        return super.hashCode() ^ ( argumentRequired ? 0 : 1 );
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ArgumentList.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ArgumentList.java
new file mode 100644
index 00000000000..e8cf157f99c
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ArgumentList.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple;
+
+import static jdk.internal.joptsimple.ParserRules.*;
+
+/**
+ * <p>Wrapper for an array of command line arguments.</p>
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class ArgumentList {
+    private final String[] arguments;
+    private int currentIndex;
+
+    ArgumentList( String... arguments ) {
+        this.arguments = arguments.clone();
+    }
+
+    boolean hasMore() {
+        return currentIndex < arguments.length;
+    }
+
+    String next() {
+        return arguments[ currentIndex++ ];
+    }
+
+    String peek() {
+        return arguments[ currentIndex ];
+    }
+
+    void treatNextAsLongOption() {
+        if ( HYPHEN_CHAR != arguments[ currentIndex ].charAt( 0 ) )
+            arguments[ currentIndex ] = DOUBLE_HYPHEN + arguments[ currentIndex ];
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/BuiltinHelpFormatter.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/BuiltinHelpFormatter.java
new file mode 100644
index 00000000000..ba72af36f90
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/BuiltinHelpFormatter.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple;
+
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import jdk.internal.joptsimple.internal.Rows;
+import jdk.internal.joptsimple.internal.Strings;
+
+import static jdk.internal.joptsimple.ParserRules.*;
+import static jdk.internal.joptsimple.internal.Classes.*;
+import static jdk.internal.joptsimple.internal.Strings.*;
+
+/**
+ * <p>A help formatter that allows configuration of overall row width and column separator width.</p>
+ *
+ * <p>The formatter produces a two-column output. The left column is for the options, and the right column for their
+ * descriptions. The formatter will allow as much space as possible for the descriptions, by minimizing the option
+ * column's width, no greater than slightly less than half the overall desired width.</p>
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class BuiltinHelpFormatter implements HelpFormatter {
+    private final Rows nonOptionRows;
+    private final Rows optionRows;
+
+    /**
+     * Makes a formatter with a pre-configured overall row width and column separator width.
+     */
+    BuiltinHelpFormatter() {
+        this( 80, 2 );
+    }
+
+    /**
+     * Makes a formatter with a given overall row width and column separator width.
+     *
+     * @param desiredOverallWidth how many characters wide to make the overall help display
+     * @param desiredColumnSeparatorWidth how many characters wide to make the separation between option column and
+     * description column
+     */
+    public BuiltinHelpFormatter( int desiredOverallWidth, int desiredColumnSeparatorWidth ) {
+        nonOptionRows = new Rows( desiredOverallWidth * 2, 0 );
+        optionRows = new Rows( desiredOverallWidth, desiredColumnSeparatorWidth );
+    }
+
+    public String format( Map<String, ? extends OptionDescriptor> options ) {
+        Comparator<OptionDescriptor> comparator =
+            new Comparator<OptionDescriptor>() {
+                public int compare( OptionDescriptor first, OptionDescriptor second ) {
+                    return first.options().iterator().next().compareTo( second.options().iterator().next() );
+                }
+            };
+
+        Set<OptionDescriptor> sorted = new TreeSet<OptionDescriptor>( comparator );
+        sorted.addAll( options.values() );
+
+        addRows( sorted );
+
+        return formattedHelpOutput();
+    }
+
+    private String formattedHelpOutput() {
+        StringBuilder formatted = new StringBuilder();
+        String nonOptionDisplay = nonOptionRows.render();
+        if ( !Strings.isNullOrEmpty( nonOptionDisplay ) )
+            formatted.append( nonOptionDisplay ).append( LINE_SEPARATOR );
+        formatted.append( optionRows.render() );
+
+        return formatted.toString();
+    }
+
+    private void addRows( Collection<? extends OptionDescriptor> options ) {
+        addNonOptionsDescription( options );
+
+        if ( options.isEmpty() )
+            optionRows.add( "No options specified", "" );
+        else {
+            addHeaders( options );
+            addOptions( options );
+        }
+
+        fitRowsToWidth();
+    }
+
+    private void addNonOptionsDescription( Collection<? extends OptionDescriptor> options ) {
+        OptionDescriptor nonOptions = findAndRemoveNonOptionsSpec( options );
+        if ( shouldShowNonOptionArgumentDisplay( nonOptions ) ) {
+            nonOptionRows.add( "Non-option arguments:", "" );
+            nonOptionRows.add(createNonOptionArgumentsDisplay(nonOptions), "");
+        }
+    }
+
+    private boolean shouldShowNonOptionArgumentDisplay( OptionDescriptor nonOptions ) {
+        return !Strings.isNullOrEmpty( nonOptions.description() )
+            || !Strings.isNullOrEmpty( nonOptions.argumentTypeIndicator() )
+            || !Strings.isNullOrEmpty( nonOptions.argumentDescription() );
+    }
+
+    private String createNonOptionArgumentsDisplay(OptionDescriptor nonOptions) {
+        StringBuilder buffer = new StringBuilder();
+        maybeAppendOptionInfo( buffer, nonOptions );
+        maybeAppendNonOptionsDescription( buffer, nonOptions );
+
+        return buffer.toString();
+    }
+
+    private void maybeAppendNonOptionsDescription( StringBuilder buffer, OptionDescriptor nonOptions ) {
+        buffer.append( buffer.length() > 0 && !Strings.isNullOrEmpty( nonOptions.description() ) ? " -- " : "" )
+            .append( nonOptions.description() );
+    }
+
+    private OptionDescriptor findAndRemoveNonOptionsSpec( Collection<? extends OptionDescriptor> options ) {
+        for ( Iterator<? extends OptionDescriptor> it = options.iterator(); it.hasNext(); ) {
+            OptionDescriptor next = it.next();
+            if ( next.representsNonOptions() ) {
+                it.remove();
+                return next;
+            }
+        }
+
+        throw new AssertionError( "no non-options argument spec" );
+    }
+
+    private void addHeaders( Collection<? extends OptionDescriptor> options ) {
+        if ( hasRequiredOption( options ) ) {
+            optionRows.add("Option (* = required)", "Description");
+            optionRows.add("---------------------", "-----------");
+        } else {
+            optionRows.add("Option", "Description");
+            optionRows.add("------", "-----------");
+        }
+    }
+
+    private boolean hasRequiredOption( Collection<? extends OptionDescriptor> options ) {
+        for ( OptionDescriptor each : options ) {
+            if ( each.isRequired() )
+                return true;
+        }
+
+        return false;
+    }
+
+    private void addOptions( Collection<? extends OptionDescriptor> options ) {
+        for ( OptionDescriptor each : options ) {
+            if ( !each.representsNonOptions() )
+                optionRows.add( createOptionDisplay( each ), createDescriptionDisplay( each ) );
+        }
+    }
+
+    private String createOptionDisplay( OptionDescriptor descriptor ) {
+        StringBuilder buffer = new StringBuilder( descriptor.isRequired() ? "* " : "" );
+
+        for ( Iterator<String> i = descriptor.options().iterator(); i.hasNext(); ) {
+            String option = i.next();
+            buffer.append( option.length() > 1 ? DOUBLE_HYPHEN : HYPHEN );
+            buffer.append( option );
+
+            if ( i.hasNext() )
+                buffer.append( ", " );
+        }
+
+        maybeAppendOptionInfo( buffer, descriptor );
+
+        return buffer.toString();
+    }
+
+    private void maybeAppendOptionInfo( StringBuilder buffer, OptionDescriptor descriptor ) {
+        String indicator = extractTypeIndicator( descriptor );
+        String description = descriptor.argumentDescription();
+        if ( indicator != null || !isNullOrEmpty( description ) )
+            appendOptionHelp( buffer, indicator, description, descriptor.requiresArgument() );
+    }
+
+    private String extractTypeIndicator( OptionDescriptor descriptor ) {
+        String indicator = descriptor.argumentTypeIndicator();
+
+        if ( !isNullOrEmpty( indicator ) && !String.class.getName().equals( indicator ) )
+            return shortNameOf( indicator );
+
+        return null;
+    }
+
+    private void appendOptionHelp( StringBuilder buffer, String typeIndicator, String description, boolean required ) {
+        if ( required )
+            appendTypeIndicator( buffer, typeIndicator, description, '<', '>' );
+        else
+            appendTypeIndicator( buffer, typeIndicator, description, '[', ']' );
+    }
+
+    private void appendTypeIndicator( StringBuilder buffer, String typeIndicator, String description,
+                                      char start, char end ) {
+        buffer.append( ' ' ).append( start );
+        if ( typeIndicator != null )
+            buffer.append( typeIndicator );
+
+        if ( !Strings.isNullOrEmpty( description ) ) {
+            if ( typeIndicator != null )
+                buffer.append( ": " );
+
+            buffer.append( description );
+        }
+
+        buffer.append( end );
+    }
+
+    private String createDescriptionDisplay( OptionDescriptor descriptor ) {
+        List<?> defaultValues = descriptor.defaultValues();
+        if ( defaultValues.isEmpty() )
+            return descriptor.description();
+
+        String defaultValuesDisplay = createDefaultValuesDisplay( defaultValues );
+        return ( descriptor.description() + ' ' + surround( "default: " + defaultValuesDisplay, '(', ')' ) ).trim();
+    }
+
+    private String createDefaultValuesDisplay( List<?> defaultValues ) {
+        return defaultValues.size() == 1 ? defaultValues.get( 0 ).toString() : defaultValues.toString();
+    }
+
+    private void fitRowsToWidth() {
+        nonOptionRows.fitToWidth();
+        optionRows.fitToWidth();
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/HelpFormatter.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/HelpFormatter.java
new file mode 100644
index 00000000000..e5dd316f1d7
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/HelpFormatter.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple;
+
+import java.util.Map;
+
+/**
+ * <p>Represents objects charged with taking a set of option descriptions and producing some help text from them.</p>
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public interface HelpFormatter {
+    /**
+     * Produces help text, given a set of option descriptors.
+     *
+     * @param options descriptors for the configured options of a parser
+     * @return text to be used as help
+     * @see OptionParser#printHelpOn(java.io.Writer)
+     * @see OptionParser#formatHelpWith(HelpFormatter)
+     */
+    String format( Map<String, ? extends OptionDescriptor> options );
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/IllegalOptionSpecificationException.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/IllegalOptionSpecificationException.java
new file mode 100644
index 00000000000..8696bf1a150
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/IllegalOptionSpecificationException.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple;
+
+import static java.util.Collections.*;
+
+/**
+ * Thrown when the option parser is asked to recognize an option with illegal characters in it.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class IllegalOptionSpecificationException extends OptionException {
+    private static final long serialVersionUID = -1L;
+
+    IllegalOptionSpecificationException( String option ) {
+        super( singletonList( option ) );
+    }
+
+    @Override
+    public String getMessage() {
+        return singleOptionMessage() + " is not a legal option character";
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/MissingRequiredOptionException.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/MissingRequiredOptionException.java
new file mode 100644
index 00000000000..2c997e2052c
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/MissingRequiredOptionException.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple;
+
+import java.util.Collection;
+
+/**
+ * Thrown when an option is marked as required, but not specified on the command line.
+ *
+ * @author <a href="https://github.com/TC1">Emils Solmanis</a>
+ */
+class MissingRequiredOptionException extends OptionException {
+    private static final long serialVersionUID = -1L;
+
+    protected MissingRequiredOptionException( Collection<String> options ) {
+        super( options );
+    }
+
+    @Override
+    public String getMessage() {
+        return "Missing required option(s) " + multipleOptionMessage();
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/MultipleArgumentsForOptionException.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/MultipleArgumentsForOptionException.java
new file mode 100644
index 00000000000..e96cea33918
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/MultipleArgumentsForOptionException.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple;
+
+import java.util.Collection;
+
+/**
+ * Thrown when asking an {@link OptionSet} for a single argument of an option when many have been specified.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class MultipleArgumentsForOptionException extends OptionException {
+    private static final long serialVersionUID = -1L;
+
+    MultipleArgumentsForOptionException( Collection<String> options ) {
+        super( options );
+    }
+
+    @Override
+    public String getMessage() {
+        return "Found multiple arguments for option " + multipleOptionMessage() + ", but you asked for only one";
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/NoArgumentOptionSpec.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/NoArgumentOptionSpec.java
new file mode 100644
index 00000000000..9647ff1bad2
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/NoArgumentOptionSpec.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple;
+
+import java.util.Collection;
+import java.util.List;
+
+import static java.util.Collections.*;
+
+/**
+ * A specification for an option that does not accept arguments.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class NoArgumentOptionSpec extends AbstractOptionSpec<Void> {
+    NoArgumentOptionSpec( String option ) {
+        this( singletonList( option ), "" );
+    }
+
+    NoArgumentOptionSpec( Collection<String> options, String description ) {
+        super( options, description );
+    }
+
+    @Override
+    void handleOption( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions,
+        String detectedArgument ) {
+
+        detectedOptions.add( this );
+    }
+
+    public boolean acceptsArguments() {
+        return false;
+    }
+
+    public boolean requiresArgument() {
+        return false;
+    }
+
+    public boolean isRequired() {
+        return false;
+    }
+
+    public String argumentDescription() {
+        return "";
+    }
+
+    public String argumentTypeIndicator() {
+        return "";
+    }
+
+    @Override
+    protected Void convert( String argument ) {
+        return null;
+    }
+
+    public List<Void> defaultValues() {
+        return emptyList();
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/NonOptionArgumentSpec.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/NonOptionArgumentSpec.java
new file mode 100644
index 00000000000..8124a05e366
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/NonOptionArgumentSpec.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple;
+
+import java.util.List;
+
+import static java.util.Arrays.*;
+import static java.util.Collections.*;
+import static jdk.internal.joptsimple.internal.Reflection.*;
+
+/**
+ * <p>Specification of a command line's non-option arguments.</p>
+ *
+ * <p>Instances are returned from {@link OptionParser} methods to allow the formation of parser directives as
+ * sentences in a "fluent interface" language. For example:</p>
+ *
+ * <pre>
+ *   <code>
+ *   OptionParser parser = new OptionParser();
+ *   parser.nonOptions( "files to be processed" ).<strong>ofType( File.class )</strong>;
+ *   </code>
+ * </pre>
+ *
+ * <p>If no methods are invoked on an instance of this class, then that instance's option will treat the non-option
+ * arguments as {@link String}s.</p>
+ *
+ * @param <V> represents the type of the non-option arguments
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class NonOptionArgumentSpec<V> extends AbstractOptionSpec<V> {
+    static final String NAME = "[arguments]";
+
+    private ValueConverter<V> converter;
+    private String argumentDescription = "";
+
+    NonOptionArgumentSpec() {
+        this("");
+    }
+
+    NonOptionArgumentSpec( String description ) {
+        super( asList( NAME ), description );
+    }
+
+    /**
+     * <p>Specifies a type to which the non-option arguments are to be converted.</p>
+     *
+     * <p>JOpt Simple accepts types that have either:</p>
+     *
+     * <ol>
+     *   <li>a public static method called {@code valueOf} which accepts a single argument of type {@link String}
+     *   and whose return type is the same as the class on which the method is declared.  The {@code java.lang}
+     *   primitive wrapper classes have such methods.</li>
+     *
+     *   <li>a public constructor which accepts a single argument of type {@link String}.</li>
+     * </ol>
+     *
+     * <p>This class converts arguments using those methods in that order; that is, {@code valueOf} would be invoked
+     * before a one-{@link String}-arg constructor would.</p>
+     *
+     * <p>Invoking this method will trump any previous calls to this method or to
+     * {@link #withValuesConvertedBy(ValueConverter)}.</p>
+     *
+     * @param <T> represents the runtime class of the desired option argument type
+     * @param argumentType desired type of arguments to this spec's option
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     * @throws NullPointerException if the type is {@code null}
+     * @throws IllegalArgumentException if the type does not have the standard conversion methods
+     */
+    @SuppressWarnings( "unchecked" )
+    public <T> NonOptionArgumentSpec<T> ofType( Class<T> argumentType ) {
+        converter = (ValueConverter<V>) findConverter( argumentType );
+        return (NonOptionArgumentSpec<T>) this;
+    }
+
+    /**
+     * <p>Specifies a converter to use to translate non-option arguments into Java objects.  This is useful
+     * when converting to types that do not have the requisite factory method or constructor for
+     * {@link #ofType(Class)}.</p>
+     *
+     * <p>Invoking this method will trump any previous calls to this method or to {@link #ofType(Class)}.
+     *
+     * @param <T> represents the runtime class of the desired non-option argument type
+     * @param aConverter the converter to use
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     * @throws NullPointerException if the converter is {@code null}
+     */
+    @SuppressWarnings( "unchecked" )
+    public final <T> NonOptionArgumentSpec<T> withValuesConvertedBy( ValueConverter<T> aConverter ) {
+        if ( aConverter == null )
+            throw new NullPointerException( "illegal null converter" );
+
+        converter = (ValueConverter<V>) aConverter;
+        return (NonOptionArgumentSpec<T>) this;
+    }
+
+    /**
+     * <p>Specifies a description for the non-option arguments that this spec represents.  This description is used
+     * when generating help information about the parser.</p>
+     *
+     * @param description describes the nature of the argument of this spec's option
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     */
+    public NonOptionArgumentSpec<V> describedAs( String description ) {
+        argumentDescription = description;
+        return this;
+    }
+
+    @Override
+    protected final V convert( String argument ) {
+        return convertWith( converter, argument );
+    }
+
+    @Override
+    void handleOption( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions,
+        String detectedArgument ) {
+
+        detectedOptions.addWithArgument( this, detectedArgument );
+    }
+
+    public List<?> defaultValues() {
+        return emptyList();
+    }
+
+    public boolean isRequired() {
+        return false;
+    }
+
+    public boolean acceptsArguments() {
+        return false;
+    }
+
+    public boolean requiresArgument() {
+        return false;
+    }
+
+    public String argumentDescription() {
+        return argumentDescription;
+    }
+
+    public String argumentTypeIndicator() {
+        return argumentTypeIndicatorFrom( converter );
+    }
+
+    public boolean representsNonOptions() {
+        return true;
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionArgumentConversionException.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionArgumentConversionException.java
new file mode 100644
index 00000000000..063b97200f8
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionArgumentConversionException.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple;
+
+import java.util.Collection;
+
+/**
+ * Thrown when a problem occurs converting an argument of an option from {@link String} to another type.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class OptionArgumentConversionException extends OptionException {
+    private static final long serialVersionUID = -1L;
+
+    private final String argument;
+
+    OptionArgumentConversionException( Collection<String> options, String argument, Throwable cause ) {
+        super( options, cause );
+
+        this.argument = argument;
+    }
+
+    @Override
+    public String getMessage() {
+        return "Cannot parse argument '" + argument + "' of option " + multipleOptionMessage();
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionDeclarer.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionDeclarer.java
new file mode 100644
index 00000000000..2a85e25901a
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionDeclarer.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.joptsimple;
+
+import java.util.Collection;
+
+/**
+ * Trains the option parser. This interface aids integration with other code which may expose declaration of options but
+ * not actual command-line parsing.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ * @see OptionParser
+ */
+public interface OptionDeclarer {
+    /**
+     * Tells the parser to recognize the given option.
+     *
+     * <p>This method returns an instance of {@link OptionSpecBuilder} to allow the formation of parser directives
+     * as sentences in a fluent interface language. For example:</p>
+     *
+     * <pre><code>
+     *   OptionDeclarer parser = new OptionParser();
+     *   parser.<strong>accepts( "c" )</strong>.withRequiredArg().ofType( Integer.class );
+     * </code></pre>
+     *
+     * <p>If no methods are invoked on the returned {@link OptionSpecBuilder}, then the parser treats the option as
+     * accepting no argument.</p>
+     *
+     * @param option the option to recognize
+     * @return an object that can be used to flesh out more detail about the option
+     * @throws OptionException if the option contains illegal characters
+     * @throws NullPointerException if the option is {@code null}
+     */
+    OptionSpecBuilder accepts( String option );
+
+    /**
+     * Tells the parser to recognize the given option.
+     *
+     * @see #accepts(String)
+     * @param option the option to recognize
+     * @param description a string that describes the purpose of the option. This is used when generating help
+     * information about the parser.
+     * @return an object that can be used to flesh out more detail about the option
+     * @throws OptionException if the option contains illegal characters
+     * @throws NullPointerException if the option is {@code null}
+     */
+    OptionSpecBuilder accepts( String option, String description );
+
+    /**
+     * Tells the parser to recognize the given options, and treat them as synonymous.
+     *
+     * @see #accepts(String)
+     * @param options the options to recognize and treat as synonymous
+     * @return an object that can be used to flesh out more detail about the options
+     * @throws OptionException if any of the options contain illegal characters
+     * @throws NullPointerException if the option list or any of its elements are {@code null}
+     */
+    OptionSpecBuilder acceptsAll( Collection<String> options );
+
+    /**
+     * Tells the parser to recognize the given options, and treat them as synonymous.
+     *
+     * @see #acceptsAll(Collection)
+     * @param options the options to recognize and treat as synonymous
+     * @param description a string that describes the purpose of the option.  This is used when generating help
+     * information about the parser.
+     * @return an object that can be used to flesh out more detail about the options
+     * @throws OptionException if any of the options contain illegal characters
+     * @throws NullPointerException if the option list or any of its elements are {@code null}
+     * @throws IllegalArgumentException if the option list is empty
+     */
+    OptionSpecBuilder acceptsAll( Collection<String> options, String description );
+
+    /**
+     * Gives an object that represents an access point for non-option arguments on a command line.
+     *
+     * @return an object that can be used to flesh out more detail about the non-option arguments
+     */
+    NonOptionArgumentSpec<String> nonOptions();
+
+    /**
+     * Gives an object that represents an access point for non-option arguments on a command line.
+     *
+     * @see #nonOptions()
+     * @param description a string that describes the purpose of the non-option arguments. This is used when generating
+     * help information about the parser.
+     * @return an object that can be used to flesh out more detail about the non-option arguments
+     */
+    NonOptionArgumentSpec<String> nonOptions( String description );
+
+    /**
+     * Tells the parser whether or not to behave "POSIX-ly correct"-ly.
+     *
+     * @param setting {@code true} if the parser should behave "POSIX-ly correct"-ly
+     */
+    void posixlyCorrect( boolean setting );
+
+    /**
+     * <p>Tells the parser to treat unrecognized options as non-option arguments.</p>
+     *
+     * <p>If not called, then the parser raises an {@link OptionException} when it encounters an unrecognized
+     * option.</p>
+     */
+    void allowsUnrecognizedOptions();
+
+    /**
+     * Tells the parser either to recognize or ignore <kbd>"-W"</kbd>-style long options.
+     *
+     * @param recognize {@code true} if the parser is to recognize the special style of long options
+     */
+    void recognizeAlternativeLongOptions( boolean recognize );
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionDescriptor.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionDescriptor.java
new file mode 100644
index 00000000000..4a6694814f3
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionDescriptor.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Describes options that an option parser recognizes, in ways that might be useful to {@linkplain HelpFormatter
+ * help screens}.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public interface OptionDescriptor {
+    /**
+     * A set of options that are mutually synonymous.
+     *
+     * @return synonymous options
+     */
+    Collection<String> options();
+
+    /**
+     * Description of this option's purpose.
+     *
+     * @return a description for the option
+     */
+    String description();
+
+    /**
+     * What values will the option take if none are specified on the command line?
+     *
+     * @return any default values for the option
+     */
+    List<?> defaultValues();
+
+    /**
+     * Is this option {@linkplain ArgumentAcceptingOptionSpec#required() required} on a command line?
+     *
+     * @return whether the option is required
+     */
+    boolean isRequired();
+
+    /**
+     * Does this option {@linkplain ArgumentAcceptingOptionSpec accept arguments}?
+     *
+     * @return whether the option accepts arguments
+     */
+    boolean acceptsArguments();
+
+    /**
+     * Does this option {@linkplain OptionSpecBuilder#withRequiredArg() require an argument}?
+     *
+     * @return whether the option requires an argument
+     */
+    boolean requiresArgument();
+
+    /**
+     * Gives a short {@linkplain ArgumentAcceptingOptionSpec#describedAs(String) description} of the option's argument.
+     *
+     * @return a description for the option's argument
+     */
+    String argumentDescription();
+
+    /**
+     * Gives an indication of the {@linkplain ArgumentAcceptingOptionSpec#ofType(Class) expected type} of the option's
+     * argument.
+     *
+     * @return a description for the option's argument type
+     */
+    String argumentTypeIndicator();
+
+    /**
+     * Tells whether this object represents the non-option arguments of a command line.
+     *
+     * @return {@code true} if this represents non-option arguments
+     */
+    boolean representsNonOptions();
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionException.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionException.java
new file mode 100644
index 00000000000..15977b63801
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionException.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import static java.util.Collections.*;
+
+import static jdk.internal.joptsimple.internal.Strings.*;
+
+/**
+ * Thrown when a problem occurs during option parsing.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public abstract class OptionException extends RuntimeException {
+    private static final long serialVersionUID = -1L;
+
+    private final List<String> options = new ArrayList<String>();
+
+    protected OptionException( Collection<String> options ) {
+        this.options.addAll( options );
+    }
+
+    protected OptionException( Collection<String> options, Throwable cause ) {
+        super( cause );
+
+        this.options.addAll( options );
+    }
+
+    /**
+     * Gives the option being considered when the exception was created.
+     *
+     * @return the option being considered when the exception was created
+     */
+    public Collection<String> options() {
+        return unmodifiableCollection( options );
+    }
+
+    protected final String singleOptionMessage() {
+        return singleOptionMessage( options.get( 0 ) );
+    }
+
+    protected final String singleOptionMessage( String option ) {
+        return SINGLE_QUOTE + option + SINGLE_QUOTE;
+    }
+
+    protected final String multipleOptionMessage() {
+        StringBuilder buffer = new StringBuilder( "[" );
+
+        for ( Iterator<String> iter = options.iterator(); iter.hasNext(); ) {
+            buffer.append( singleOptionMessage( iter.next() ) );
+            if ( iter.hasNext() )
+                buffer.append( ", " );
+        }
+
+        buffer.append( ']' );
+
+        return buffer.toString();
+    }
+
+    static OptionException unrecognizedOption( String option ) {
+        return new UnrecognizedOptionException( option );
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionMissingRequiredArgumentException.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionMissingRequiredArgumentException.java
new file mode 100644
index 00000000000..f667b446527
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionMissingRequiredArgumentException.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple;
+
+import java.util.Collection;
+
+/**
+ * Thrown when the option parser discovers an option that requires an argument, but that argument is missing.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class OptionMissingRequiredArgumentException extends OptionException {
+    private static final long serialVersionUID = -1L;
+
+    OptionMissingRequiredArgumentException( Collection<String> options ) {
+        super( options );
+    }
+
+    @Override
+    public String getMessage() {
+        return "Option " + multipleOptionMessage() + " requires an argument";
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionParser.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionParser.java
new file mode 100644
index 00000000000..c6c319a329a
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionParser.java
@@ -0,0 +1,580 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple;
+
+import jdk.internal.joptsimple.internal.AbbreviationMap;
+import jdk.internal.joptsimple.util.KeyValuePair;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static java.util.Collections.*;
+import static jdk.internal.joptsimple.OptionException.*;
+import static jdk.internal.joptsimple.OptionParserState.*;
+import static jdk.internal.joptsimple.ParserRules.*;
+
+/**
+ * <p>Parses command line arguments, using a syntax that attempts to take from the best of POSIX {@code getopt()}
+ * and GNU {@code getopt_long()}.</p>
+ *
+ * <p>This parser supports short options and long options.</p>
+ *
+ * <ul>
+ *   <li><dfn>Short options</dfn> begin with a single hyphen ("<kbd>-</kbd>") followed by a single letter or digit,
+ *   or question mark ("<kbd>?</kbd>"), or dot ("<kbd>.</kbd>").</li>
+ *
+ *   <li>Short options can accept single arguments. The argument can be made required or optional. The option's
+ *   argument can occur:
+ *     <ul>
+ *       <li>in the slot after the option, as in <kbd>-d /tmp</kbd></li>
+ *       <li>right up against the option, as in <kbd>-d/tmp</kbd></li>
+ *       <li>right up against the option separated by an equals sign (<kbd>"="</kbd>), as in <kbd>-d=/tmp</kbd></li>
+ *     </ul>
+ *   To specify <em>n</em> arguments for an option, specify the option <em>n</em> times, once for each argument,
+ *   as in <kbd>-d /tmp -d /var -d /opt</kbd>; or, when using the
+ *   {@linkplain ArgumentAcceptingOptionSpec#withValuesSeparatedBy(char) "separated values"} clause of the "fluent
+ *   interface" (see below), give multiple values separated by a given character as a single argument to the
+ *   option.</li>
+ *
+ *   <li>Short options can be clustered, so that <kbd>-abc</kbd> is treated as <kbd>-a -b -c</kbd>. If a short option
+ *   in the cluster can accept an argument, the remaining characters are interpreted as the argument for that
+ *   option.</li>
+ *
+ *   <li>An argument consisting only of two hyphens (<kbd>"--"</kbd>) signals that the remaining arguments are to be
+ *   treated as non-options.</li>
+ *
+ *   <li>An argument consisting only of a single hyphen is considered a non-option argument (though it can be an
+ *   argument of an option). Many Unix programs treat single hyphens as stand-ins for the standard input or standard
+ *   output streams.</li>
+ *
+ *   <li><dfn>Long options</dfn> begin with two hyphens (<kbd>"--"</kbd>), followed by multiple letters, digits,
+ *   hyphens, question marks, or dots. A hyphen cannot be the first character of a long option specification when
+ *   configuring the parser.</li>
+ *
+ *   <li>You can abbreviate long options, so long as the abbreviation is unique.</li>
+ *
+ *   <li>Long options can accept single arguments.  The argument can be made required or optional.  The option's
+ *   argument can occur:
+ *     <ul>
+ *       <li>in the slot after the option, as in <kbd>--directory /tmp</kbd></li>
+ *       <li>right up against the option separated by an equals sign (<kbd>"="</kbd>), as in
+ *       <kbd>--directory=/tmp</kbd>
+ *     </ul>
+ *   Specify multiple arguments for a long option in the same manner as for short options (see above).</li>
+ *
+ *   <li>You can use a single hyphen (<kbd>"-"</kbd>) instead of a double hyphen (<kbd>"--"</kbd>) for a long
+ *   option.</li>
+ *
+ *   <li>The option <kbd>-W</kbd> is reserved.  If you tell the parser to {@linkplain
+ *   #recognizeAlternativeLongOptions(boolean) recognize alternative long options}, then it will treat, for example,
+ *   <kbd>-W foo=bar</kbd> as the long option <kbd>foo</kbd> with argument <kbd>bar</kbd>, as though you had written
+ *   <kbd>--foo=bar</kbd>.</li>
+ *
+ *   <li>You can specify <kbd>-W</kbd> as a valid short option, or use it as an abbreviation for a long option, but
+ *   {@linkplain #recognizeAlternativeLongOptions(boolean) recognizing alternative long options} will always supersede
+ *   this behavior.</li>
+ *
+ *   <li>You can specify a given short or long option multiple times on a single command line. The parser collects
+ *   any arguments specified for those options as a list.</li>
+ *
+ *   <li>If the parser detects an option whose argument is optional, and the next argument "looks like" an option,
+ *   that argument is not treated as the argument to the option, but as a potentially valid option. If, on the other
+ *   hand, the optional argument is typed as a derivative of {@link Number}, then that argument is treated as the
+ *   negative number argument of the option, even if the parser recognizes the corresponding numeric option.
+ *   For example:
+ *   <pre><code>
+ *     OptionParser parser = new OptionParser();
+ *     parser.accepts( "a" ).withOptionalArg().ofType( Integer.class );
+ *     parser.accepts( "2" );
+ *     OptionSet options = parser.parse( "-a", "-2" );
+ *   </code></pre>
+ *   In this case, the option set contains <kbd>"a"</kbd> with argument <kbd>-2</kbd>, not both <kbd>"a"</kbd> and
+ *   <kbd>"2"</kbd>. Swapping the elements in the <em>args</em> array gives the latter.</li>
+ * </ul>
+ *
+ * <p>There are two ways to tell the parser what options to recognize:</p>
+ *
+ * <ol>
+ *   <li>A "fluent interface"-style API for specifying options, available since version 2. Sentences in this fluent
+ *   interface language begin with a call to {@link #accepts(String) accepts} or {@link #acceptsAll(Collection)
+ *   acceptsAll} methods; calls on the ensuing chain of objects describe whether the options can take an argument,
+ *   whether the argument is required or optional, to what type arguments of the options should be converted if any,
+ *   etc. Since version 3, these calls return an instance of {@link OptionSpec}, which can subsequently be used to
+ *   retrieve the arguments of the associated option in a type-safe manner.</li>
+ *
+ *   <li>Since version 1, a more concise way of specifying short options has been to use the special {@linkplain
+ *   #OptionParser(String) constructor}. Arguments of options specified in this manner will be of type {@link String}.
+ *   Here are the rules for the format of the specification strings this constructor accepts:
+ *
+ *     <ul>
+ *       <li>Any letter or digit is treated as an option character.</li>
+ *
+ *       <li>An option character can be immediately followed by an asterisk (*) to indicate that the option is a
+ *       "help" option.</li>
+ *
+ *       <li>If an option character (with possible trailing asterisk) is followed by a single colon (<kbd>":"</kbd>),
+ *       then the option requires an argument.</li>
+ *
+ *       <li>If an option character (with possible trailing asterisk) is followed by two colons (<kbd>"::"</kbd>),
+ *       then the option accepts an optional argument.</li>
+ *
+ *       <li>Otherwise, the option character accepts no argument.</li>
+ *
+ *       <li>If the option specification string begins with a plus sign (<kbd>"+"</kbd>), the parser will behave
+ *       "POSIX-ly correct".</li>
+ *
+ *       <li>If the option specification string contains the sequence <kbd>"W;"</kbd> (capital W followed by a
+ *       semicolon), the parser will recognize the alternative form of long options.</li>
+ *     </ul>
+ *   </li>
+ * </ol>
+ *
+ * <p>Each of the options in a list of options given to {@link #acceptsAll(Collection) acceptsAll} is treated as a
+ * synonym of the others.  For example:
+ *   <pre>
+ *     <code>
+ *     OptionParser parser = new OptionParser();
+ *     parser.acceptsAll( asList( "w", "interactive", "confirmation" ) );
+ *     OptionSet options = parser.parse( "-w" );
+ *     </code>
+ *   </pre>
+ * In this case, <code>options.{@link OptionSet#has(String) has}</code> would answer {@code true} when given arguments
+ * <kbd>"w"</kbd>, <kbd>"interactive"</kbd>, and <kbd>"confirmation"</kbd>. The {@link OptionSet} would give the same
+ * responses to these arguments for its other methods as well.</p>
+ *
+ * <p>By default, as with GNU {@code getopt()}, the parser allows intermixing of options and non-options. If, however,
+ * the parser has been created to be "POSIX-ly correct", then the first argument that does not look lexically like an
+ * option, and is not a required argument of a preceding option, signals the end of options. You can still bind
+ * optional arguments to their options using the abutting (for short options) or <kbd>=</kbd> syntax.</p>
+ *
+ * <p>Unlike GNU {@code getopt()}, this parser does not honor the environment variable {@code POSIXLY_CORRECT}.
+ * "POSIX-ly correct" parsers are configured by either:</p>
+ *
+ * <ol>
+ *   <li>using the method {@link #posixlyCorrect(boolean)}, or</li>
+ *
+ *   <li>using the {@linkplain #OptionParser(String) constructor} with an argument whose first character is a plus sign
+ *   (<kbd>"+"</kbd>)</li>
+ * </ol>
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ * @see <a href="http://www.gnu.org/software/libc/manual">The GNU C Library</a>
+ */
+public class OptionParser implements OptionDeclarer {
+    private final AbbreviationMap<AbstractOptionSpec<?>> recognizedOptions;
+    private final Map<Collection<String>, Set<OptionSpec<?>>> requiredIf;
+    private final Map<Collection<String>, Set<OptionSpec<?>>> requiredUnless;
+    private OptionParserState state;
+    private boolean posixlyCorrect;
+    private boolean allowsUnrecognizedOptions;
+    private HelpFormatter helpFormatter = new BuiltinHelpFormatter();
+
+    /**
+     * Creates an option parser that initially recognizes no options, and does not exhibit "POSIX-ly correct"
+     * behavior.
+     */
+    public OptionParser() {
+        recognizedOptions = new AbbreviationMap<AbstractOptionSpec<?>>();
+        requiredIf = new HashMap<Collection<String>, Set<OptionSpec<?>>>();
+        requiredUnless = new HashMap<Collection<String>, Set<OptionSpec<?>>>();
+        state = moreOptions( false );
+
+        recognize( new NonOptionArgumentSpec<String>() );
+    }
+
+    /**
+     * Creates an option parser and configures it to recognize the short options specified in the given string.
+     *
+     * Arguments of options specified this way will be of type {@link String}.
+     *
+     * @param optionSpecification an option specification
+     * @throws NullPointerException if {@code optionSpecification} is {@code null}
+     * @throws OptionException if the option specification contains illegal characters or otherwise cannot be
+     * recognized
+     */
+    public OptionParser( String optionSpecification ) {
+        this();
+
+        new OptionSpecTokenizer( optionSpecification ).configure( this );
+    }
+
+    public OptionSpecBuilder accepts( String option ) {
+        return acceptsAll( singletonList( option ) );
+    }
+
+    public OptionSpecBuilder accepts( String option, String description ) {
+        return acceptsAll( singletonList( option ), description );
+    }
+
+    public OptionSpecBuilder acceptsAll( Collection<String> options ) {
+        return acceptsAll( options, "" );
+    }
+
+    public OptionSpecBuilder acceptsAll( Collection<String> options, String description ) {
+        if ( options.isEmpty() )
+            throw new IllegalArgumentException( "need at least one option" );
+
+        ensureLegalOptions( options );
+
+        return new OptionSpecBuilder( this, options, description );
+    }
+
+    public NonOptionArgumentSpec<String> nonOptions() {
+        NonOptionArgumentSpec<String> spec = new NonOptionArgumentSpec<String>();
+
+        recognize( spec );
+
+        return spec;
+    }
+
+    public NonOptionArgumentSpec<String> nonOptions( String description ) {
+        NonOptionArgumentSpec<String> spec = new NonOptionArgumentSpec<String>( description );
+
+        recognize( spec );
+
+        return spec;
+    }
+
+    public void posixlyCorrect( boolean setting ) {
+        posixlyCorrect = setting;
+        state = moreOptions( setting );
+    }
+
+    boolean posixlyCorrect() {
+        return posixlyCorrect;
+    }
+
+    public void allowsUnrecognizedOptions() {
+        allowsUnrecognizedOptions = true;
+    }
+
+    boolean doesAllowsUnrecognizedOptions() {
+        return allowsUnrecognizedOptions;
+    }
+
+    public void recognizeAlternativeLongOptions( boolean recognize ) {
+        if ( recognize )
+            recognize( new AlternativeLongOptionSpec() );
+        else
+            recognizedOptions.remove( String.valueOf( RESERVED_FOR_EXTENSIONS ) );
+    }
+
+    void recognize( AbstractOptionSpec<?> spec ) {
+        recognizedOptions.putAll( spec.options(), spec );
+    }
+
+    /**
+     * Writes information about the options this parser recognizes to the given output sink.
+     *
+     * The output sink is flushed, but not closed.
+     *
+     * @param sink the sink to write information to
+     * @throws IOException if there is a problem writing to the sink
+     * @throws NullPointerException if {@code sink} is {@code null}
+     * @see #printHelpOn(Writer)
+     */
+    public void printHelpOn( OutputStream sink ) throws IOException {
+        printHelpOn( new OutputStreamWriter( sink ) );
+    }
+
+    /**
+     * Writes information about the options this parser recognizes to the given output sink.
+     *
+     * The output sink is flushed, but not closed.
+     *
+     * @param sink the sink to write information to
+     * @throws IOException if there is a problem writing to the sink
+     * @throws NullPointerException if {@code sink} is {@code null}
+     * @see #printHelpOn(OutputStream)
+     */
+    public void printHelpOn( Writer sink ) throws IOException {
+        sink.write( helpFormatter.format( recognizedOptions.toJavaUtilMap() ) );
+        sink.flush();
+    }
+
+    /**
+     * Tells the parser to use the given formatter when asked to {@linkplain #printHelpOn(java.io.Writer) print help}.
+     *
+     * @param formatter the formatter to use for printing help
+     * @throws NullPointerException if the formatter is {@code null}
+     */
+    public void formatHelpWith( HelpFormatter formatter ) {
+        if ( formatter == null )
+            throw new NullPointerException();
+
+        helpFormatter = formatter;
+    }
+
+    /**
+     * Retrieves all the options which have been configured for the parser.
+     *
+     * @return a {@link Map} containing all the configured options and their corresponding {@link OptionSpec}
+     */
+    public Map<String, OptionSpec<?>> recognizedOptions() {
+        return new HashMap<String, OptionSpec<?>>( recognizedOptions.toJavaUtilMap() );
+    }
+
+    /**
+     * Parses the given command line arguments according to the option specifications given to the parser.
+     *
+     * @param arguments arguments to parse
+     * @return an {@link OptionSet} describing the parsed options, their arguments, and any non-option arguments found
+     * @throws OptionException if problems are detected while parsing
+     * @throws NullPointerException if the argument list is {@code null}
+     */
+    public OptionSet parse( String... arguments ) {
+        ArgumentList argumentList = new ArgumentList( arguments );
+        OptionSet detected = new OptionSet( recognizedOptions.toJavaUtilMap() );
+        detected.add( recognizedOptions.get( NonOptionArgumentSpec.NAME ) );
+
+        while ( argumentList.hasMore() )
+            state.handleArgument( this, argumentList, detected );
+
+        reset();
+
+        ensureRequiredOptions( detected );
+
+        return detected;
+    }
+
+    private void ensureRequiredOptions( OptionSet options ) {
+        Collection<String> missingRequiredOptions = missingRequiredOptions( options );
+        boolean helpOptionPresent = isHelpOptionPresent( options );
+
+        if ( !missingRequiredOptions.isEmpty() && !helpOptionPresent )
+            throw new MissingRequiredOptionException( missingRequiredOptions );
+    }
+
+    private Collection<String> missingRequiredOptions( OptionSet options ) {
+        Collection<String> missingRequiredOptions = new HashSet<String>();
+
+        for ( AbstractOptionSpec<?> each : recognizedOptions.toJavaUtilMap().values() ) {
+            if ( each.isRequired() && !options.has( each ) )
+                missingRequiredOptions.addAll( each.options() );
+        }
+
+        for ( Map.Entry<Collection<String>, Set<OptionSpec<?>>> eachEntry : requiredIf.entrySet() ) {
+            AbstractOptionSpec<?> required = specFor( eachEntry.getKey().iterator().next() );
+
+            if ( optionsHasAnyOf( options, eachEntry.getValue() ) && !options.has( required ) ) {
+                missingRequiredOptions.addAll( required.options() );
+            }
+        }
+
+        for ( Map.Entry<Collection<String>, Set<OptionSpec<?>>> eachEntry : requiredUnless.entrySet() ) {
+            AbstractOptionSpec<?> required = specFor( eachEntry.getKey().iterator().next() );
+
+            if ( !optionsHasAnyOf( options, eachEntry.getValue() ) && !options.has( required ) ) {
+                missingRequiredOptions.addAll( required.options() );
+            }
+        }
+
+        return missingRequiredOptions;
+    }
+
+    private boolean optionsHasAnyOf( OptionSet options, Collection<OptionSpec<?>> specs ) {
+        for ( OptionSpec<?> each : specs ) {
+            if ( options.has( each ) )
+                return true;
+        }
+
+        return false;
+    }
+
+    private boolean isHelpOptionPresent( OptionSet options ) {
+        boolean helpOptionPresent = false;
+        for ( AbstractOptionSpec<?> each : recognizedOptions.toJavaUtilMap().values() ) {
+            if ( each.isForHelp() && options.has( each ) ) {
+                helpOptionPresent = true;
+                break;
+            }
+        }
+        return helpOptionPresent;
+    }
+
+    void handleLongOptionToken( String candidate, ArgumentList arguments, OptionSet detected ) {
+        KeyValuePair optionAndArgument = parseLongOptionWithArgument( candidate );
+
+        if ( !isRecognized( optionAndArgument.key ) )
+            throw unrecognizedOption( optionAndArgument.key );
+
+        AbstractOptionSpec<?> optionSpec = specFor( optionAndArgument.key );
+        optionSpec.handleOption( this, arguments, detected, optionAndArgument.value );
+    }
+
+    void handleShortOptionToken( String candidate, ArgumentList arguments, OptionSet detected ) {
+        KeyValuePair optionAndArgument = parseShortOptionWithArgument( candidate );
+
+        if ( isRecognized( optionAndArgument.key ) ) {
+            specFor( optionAndArgument.key ).handleOption( this, arguments, detected, optionAndArgument.value );
+        }
+        else
+            handleShortOptionCluster( candidate, arguments, detected );
+    }
+
+    private void handleShortOptionCluster( String candidate, ArgumentList arguments, OptionSet detected ) {
+        char[] options = extractShortOptionsFrom( candidate );
+        validateOptionCharacters( options );
+
+        for ( int i = 0; i < options.length; i++ ) {
+            AbstractOptionSpec<?> optionSpec = specFor( options[ i ] );
+
+            if ( optionSpec.acceptsArguments() && options.length > i + 1 ) {
+                String detectedArgument = String.valueOf( options, i + 1, options.length - 1 - i );
+                optionSpec.handleOption( this, arguments, detected, detectedArgument );
+                break;
+            }
+
+            optionSpec.handleOption( this, arguments, detected, null );
+        }
+    }
+
+    void handleNonOptionArgument( String candidate, ArgumentList arguments, OptionSet detectedOptions ) {
+        specFor( NonOptionArgumentSpec.NAME ).handleOption( this, arguments, detectedOptions, candidate );
+    }
+
+    void noMoreOptions() {
+        state = OptionParserState.noMoreOptions();
+    }
+
+    boolean looksLikeAnOption( String argument ) {
+        return isShortOptionToken( argument ) || isLongOptionToken( argument );
+    }
+
+    boolean isRecognized( String option ) {
+        return recognizedOptions.contains( option );
+    }
+
+    void requiredIf( Collection<String> precedentSynonyms, String required ) {
+        requiredIf( precedentSynonyms, specFor( required ) );
+    }
+
+    void requiredIf( Collection<String> precedentSynonyms, OptionSpec<?> required ) {
+        putRequiredOption( precedentSynonyms, required, requiredIf );
+    }
+
+    void requiredUnless( Collection<String> precedentSynonyms, String required ) {
+        requiredUnless( precedentSynonyms, specFor( required ) );
+    }
+
+    void requiredUnless( Collection<String> precedentSynonyms, OptionSpec<?> required ) {
+        putRequiredOption( precedentSynonyms, required, requiredUnless );
+    }
+
+    private void putRequiredOption( Collection<String> precedentSynonyms, OptionSpec<?> required,
+        Map<Collection<String>, Set<OptionSpec<?>>> target ) {
+
+        for ( String each : precedentSynonyms ) {
+            AbstractOptionSpec<?> spec = specFor( each );
+            if ( spec == null )
+                throw new UnconfiguredOptionException( precedentSynonyms );
+        }
+
+        Set<OptionSpec<?>> associated = target.get( precedentSynonyms );
+        if ( associated == null ) {
+            associated = new HashSet<OptionSpec<?>>();
+            target.put( precedentSynonyms, associated );
+        }
+
+        associated.add( required );
+    }
+
+    private AbstractOptionSpec<?> specFor( char option ) {
+        return specFor( String.valueOf( option ) );
+    }
+
+    private AbstractOptionSpec<?> specFor( String option ) {
+        return recognizedOptions.get( option );
+    }
+
+    private void reset() {
+        state = moreOptions( posixlyCorrect );
+    }
+
+    private static char[] extractShortOptionsFrom( String argument ) {
+        char[] options = new char[ argument.length() - 1 ];
+        argument.getChars( 1, argument.length(), options, 0 );
+
+        return options;
+    }
+
+    private void validateOptionCharacters( char[] options ) {
+        for ( char each : options ) {
+            String option = String.valueOf( each );
+
+            if ( !isRecognized( option ) )
+                throw unrecognizedOption( option );
+
+            if ( specFor( option ).acceptsArguments() )
+                return;
+        }
+    }
+
+    private static KeyValuePair parseLongOptionWithArgument( String argument ) {
+        return KeyValuePair.valueOf( argument.substring( 2 ) );
+    }
+
+    private static KeyValuePair parseShortOptionWithArgument( String argument ) {
+        return KeyValuePair.valueOf( argument.substring( 1 ) );
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionParserState.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionParserState.java
new file mode 100644
index 00000000000..7fdbcbf3618
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionParserState.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple;
+
+import static jdk.internal.joptsimple.ParserRules.*;
+
+/**
+ * Abstraction of parser state; mostly serves to model how a parser behaves depending on whether end-of-options
+ * has been detected.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+abstract class OptionParserState {
+    static OptionParserState noMoreOptions() {
+        return new OptionParserState() {
+            @Override
+            protected void handleArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions ) {
+                parser.handleNonOptionArgument( arguments.next(), arguments, detectedOptions );
+            }
+        };
+    }
+
+    static OptionParserState moreOptions( final boolean posixlyCorrect ) {
+        return new OptionParserState() {
+            @Override
+            protected void handleArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions ) {
+                String candidate = arguments.next();
+                try {
+                    if ( isOptionTerminator( candidate ) ) {
+                        parser.noMoreOptions();
+                        return;
+                    } else if ( isLongOptionToken( candidate ) ) {
+                        parser.handleLongOptionToken( candidate, arguments, detectedOptions );
+                        return;
+                    } else if ( isShortOptionToken( candidate ) ) {
+                        parser.handleShortOptionToken( candidate, arguments, detectedOptions );
+                        return;
+                    }
+                } catch ( UnrecognizedOptionException e ) {
+                    if ( !parser.doesAllowsUnrecognizedOptions() )
+                        throw e;
+                }
+
+                if ( posixlyCorrect )
+                    parser.noMoreOptions();
+
+                parser.handleNonOptionArgument( candidate, arguments, detectedOptions );
+            }
+        };
+    }
+
+    protected abstract void handleArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions );
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionSet.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionSet.java
new file mode 100644
index 00000000000..71061156fa0
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionSet.java
@@ -0,0 +1,351 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple;
+
+import java.util.*;
+
+import static java.util.Collections.*;
+
+import static jdk.internal.joptsimple.internal.Objects.*;
+
+/**
+ * Representation of a group of detected command line options, their arguments, and non-option arguments.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class OptionSet {
+    private final List<OptionSpec<?>> detectedSpecs;
+    private final Map<String, AbstractOptionSpec<?>> detectedOptions;
+    private final Map<AbstractOptionSpec<?>, List<String>> optionsToArguments;
+    private final Map<String, AbstractOptionSpec<?>> recognizedSpecs;
+    private final Map<String, List<?>> defaultValues;
+
+    /*
+     * Package-private because clients don't create these.
+     */
+    OptionSet( Map<String, AbstractOptionSpec<?>> recognizedSpecs ) {
+        detectedSpecs = new ArrayList<OptionSpec<?>>();
+        detectedOptions = new HashMap<String, AbstractOptionSpec<?>>();
+        optionsToArguments = new IdentityHashMap<AbstractOptionSpec<?>, List<String>>();
+        defaultValues = defaultValues( recognizedSpecs );
+        this.recognizedSpecs = recognizedSpecs;
+    }
+
+    /**
+     * Tells whether any options were detected.
+     *
+     * @return {@code true} if any options were detected
+     */
+    public boolean hasOptions() {
+        return !detectedOptions.isEmpty();
+    }
+
+    /**
+     * Tells whether the given option was detected.
+     *
+     * @param option the option to search for
+     * @return {@code true} if the option was detected
+     * @see #has(OptionSpec)
+     */
+    public boolean has( String option ) {
+        return detectedOptions.containsKey( option );
+    }
+
+    /**
+     * Tells whether the given option was detected.
+     *
+     * <p>This method recognizes only instances of options returned from the fluent interface methods.</p>
+     *
+     * <p>Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[])} default argument value}
+     * for an option does not cause this method to return {@code true} if the option was not detected on the command
+     * line.</p>
+     *
+     * @param option the option to search for
+     * @return {@code true} if the option was detected
+     * @see #has(String)
+     */
+    public boolean has( OptionSpec<?> option ) {
+        return optionsToArguments.containsKey( option );
+    }
+
+    /**
+     * Tells whether there are any arguments associated with the given option.
+     *
+     * @param option the option to search for
+     * @return {@code true} if the option was detected and at least one argument was detected for the option
+     * @see #hasArgument(OptionSpec)
+     */
+    public boolean hasArgument( String option ) {
+        AbstractOptionSpec<?> spec = detectedOptions.get( option );
+        return spec != null && hasArgument( spec );
+    }
+
+    /**
+     * Tells whether there are any arguments associated with the given option.
+     *
+     * <p>This method recognizes only instances of options returned from the fluent interface methods.</p>
+     *
+     * <p>Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[]) default argument value}
+     * for an option does not cause this method to return {@code true} if the option was not detected on the command
+     * line, or if the option can take an optional argument but did not have one on the command line.</p>
+     *
+     * @param option the option to search for
+     * @return {@code true} if the option was detected and at least one argument was detected for the option
+     * @throws NullPointerException if {@code option} is {@code null}
+     * @see #hasArgument(String)
+     */
+    public boolean hasArgument( OptionSpec<?> option ) {
+        ensureNotNull( option );
+
+        List<String> values = optionsToArguments.get( option );
+        return values != null && !values.isEmpty();
+    }
+
+    /**
+     * Gives the argument associated with the given option.  If the option was given an argument type, the argument
+     * will take on that type; otherwise, it will be a {@link String}.
+     *
+     * <p>Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[]) default argument value}
+     * for an option will cause this method to return that default value even if the option was not detected on the
+     * command line, or if the option can take an optional argument but did not have one on the command line.</p>
+     *
+     * @param option the option to search for
+     * @return the argument of the given option; {@code null} if no argument is present, or that option was not
+     * detected
+     * @throws NullPointerException if {@code option} is {@code null}
+     * @throws OptionException if more than one argument was detected for the option
+     */
+    public Object valueOf( String option ) {
+        ensureNotNull( option );
+
+        AbstractOptionSpec<?> spec = detectedOptions.get( option );
+        if ( spec == null ) {
+            List<?> defaults = defaultValuesFor( option );
+            return defaults.isEmpty() ? null : defaults.get( 0 );
+        }
+
+        return valueOf( spec );
+    }
+
+    /**
+     * Gives the argument associated with the given option.
+     *
+     * <p>This method recognizes only instances of options returned from the fluent interface methods.</p>
+     *
+     * @param <V> represents the type of the arguments the given option accepts
+     * @param option the option to search for
+     * @return the argument of the given option; {@code null} if no argument is present, or that option was not
+     * detected
+     * @throws OptionException if more than one argument was detected for the option
+     * @throws NullPointerException if {@code option} is {@code null}
+     * @throws ClassCastException if the arguments of this option are not of the expected type
+     */
+    public <V> V valueOf( OptionSpec<V> option ) {
+        ensureNotNull( option );
+
+        List<V> values = valuesOf( option );
+        switch ( values.size() ) {
+            case 0:
+                return null;
+            case 1:
+                return values.get( 0 );
+            default:
+                throw new MultipleArgumentsForOptionException( option.options() );
+        }
+    }
+
+    /**
+     * <p>Gives any arguments associated with the given option.  If the option was given an argument type, the
+     * arguments will take on that type; otherwise, they will be {@link String}s.</p>
+     *
+     * @param option the option to search for
+     * @return the arguments associated with the option, as a list of objects of the type given to the arguments; an
+     * empty list if no such arguments are present, or if the option was not detected
+     * @throws NullPointerException if {@code option} is {@code null}
+     */
+    public List<?> valuesOf( String option ) {
+        ensureNotNull( option );
+
+        AbstractOptionSpec<?> spec = detectedOptions.get( option );
+        return spec == null ? defaultValuesFor( option ) : valuesOf( spec );
+    }
+
+    /**
+     * <p>Gives any arguments associated with the given option.  If the option was given an argument type, the
+     * arguments will take on that type; otherwise, they will be {@link String}s.</p>
+     *
+     * <p>This method recognizes only instances of options returned from the fluent interface methods.</p>
+     *
+     * @param <V> represents the type of the arguments the given option accepts
+     * @param option the option to search for
+     * @return the arguments associated with the option; an empty list if no such arguments are present, or if the
+     * option was not detected
+     * @throws NullPointerException if {@code option} is {@code null}
+     * @throws OptionException if there is a problem converting the option's arguments to the desired type; for
+     * example, if the type does not implement a correct conversion constructor or method
+     */
+    public <V> List<V> valuesOf( OptionSpec<V> option ) {
+        ensureNotNull( option );
+
+        List<String> values = optionsToArguments.get( option );
+        if ( values == null || values.isEmpty() )
+            return defaultValueFor( option );
+
+        AbstractOptionSpec<V> spec = (AbstractOptionSpec<V>) option;
+        List<V> convertedValues = new ArrayList<V>();
+        for ( String each : values )
+            convertedValues.add( spec.convert( each ) );
+
+        return unmodifiableList( convertedValues );
+    }
+
+    /**
+     * Gives the set of options that were detected, in the form of {@linkplain OptionSpec}s, in the order in which the
+     * options were found on the command line.
+     *
+     * @return the set of detected command line options
+     */
+    public List<OptionSpec<?>> specs() {
+        List<OptionSpec<?>> specs = detectedSpecs;
+        specs.remove( detectedOptions.get( NonOptionArgumentSpec.NAME ) );
+
+        return unmodifiableList( specs );
+    }
+
+    /**
+     * Gives all declared options as a map of string to {@linkplain OptionSpec}.
+     *
+     * @return the declared options as a map
+     */
+    public Map<OptionSpec<?>, List<?>> asMap() {
+        Map<OptionSpec<?>, List<?>> map = new HashMap<OptionSpec<?>, List<?>>();
+        for ( AbstractOptionSpec<?> spec : recognizedSpecs.values() )
+            if ( !spec.representsNonOptions() )
+                map.put( spec, valuesOf( spec ) );
+        return unmodifiableMap( map );
+    }
+
+    /**
+     * @return the detected non-option arguments
+     */
+    public List<?> nonOptionArguments() {
+        return unmodifiableList( valuesOf( detectedOptions.get( NonOptionArgumentSpec.NAME ) ) );
+    }
+
+    void add( AbstractOptionSpec<?> spec ) {
+        addWithArgument( spec, null );
+    }
+
+    void addWithArgument( AbstractOptionSpec<?> spec, String argument ) {
+        detectedSpecs.add( spec );
+
+        for ( String each : spec.options() )
+            detectedOptions.put( each, spec );
+
+        List<String> optionArguments = optionsToArguments.get( spec );
+
+        if ( optionArguments == null ) {
+            optionArguments = new ArrayList<String>();
+            optionsToArguments.put( spec, optionArguments );
+        }
+
+        if ( argument != null )
+            optionArguments.add( argument );
+    }
+
+    @Override
+    public boolean equals( Object that ) {
+        if ( this == that )
+            return true;
+
+        if ( that == null || !getClass().equals( that.getClass() ) )
+            return false;
+
+        OptionSet other = (OptionSet) that;
+        Map<AbstractOptionSpec<?>, List<String>> thisOptionsToArguments =
+            new HashMap<AbstractOptionSpec<?>, List<String>>( optionsToArguments );
+        Map<AbstractOptionSpec<?>, List<String>> otherOptionsToArguments =
+            new HashMap<AbstractOptionSpec<?>, List<String>>( other.optionsToArguments );
+        return detectedOptions.equals( other.detectedOptions )
+            && thisOptionsToArguments.equals( otherOptionsToArguments );
+    }
+
+    @Override
+    public int hashCode() {
+        Map<AbstractOptionSpec<?>, List<String>> thisOptionsToArguments =
+            new HashMap<AbstractOptionSpec<?>, List<String>>( optionsToArguments );
+        return detectedOptions.hashCode() ^ thisOptionsToArguments.hashCode();
+    }
+
+    @SuppressWarnings( "unchecked" )
+    private <V> List<V> defaultValuesFor( String option ) {
+        if ( defaultValues.containsKey( option ) )
+            return (List<V>) defaultValues.get( option );
+
+        return emptyList();
+    }
+
+    private <V> List<V> defaultValueFor( OptionSpec<V> option ) {
+        return defaultValuesFor( option.options().iterator().next() );
+    }
+
+    private static Map<String, List<?>> defaultValues( Map<String, AbstractOptionSpec<?>> recognizedSpecs ) {
+        Map<String, List<?>> defaults = new HashMap<String, List<?>>();
+        for ( Map.Entry<String, AbstractOptionSpec<?>> each : recognizedSpecs.entrySet() )
+            defaults.put( each.getKey(), each.getValue().defaultValues() );
+        return defaults;
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionSpec.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionSpec.java
new file mode 100644
index 00000000000..666230b05d4
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionSpec.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Describes options that an option parser recognizes.
+ *
+ * <p>Instances of this interface are returned by the "fluent interface" methods to allow retrieval of option arguments
+ * in a type-safe manner.  Here's an example:</p>
+ *
+ * <pre><code>
+ *     OptionParser parser = new OptionParser();
+ *     <strong>OptionSpec&lt;Integer&gt;</strong> count =
+ *         parser.accepts( "count" ).withRequiredArg().ofType( Integer.class );
+ *     OptionSet options = parser.parse( "--count", "2" );
+ *     assert options.has( count );
+ *     int countValue = options.valueOf( count );
+ *     assert countValue == count.value( options );
+ *     List&lt;Integer&gt; countValues = options.valuesOf( count );
+ *     assert countValues.equals( count.values( options ) );
+ * </code></pre>
+ *
+ * @param <V> represents the type of the arguments this option accepts
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public interface OptionSpec<V> {
+    /**
+     * Gives any arguments associated with the given option in the given set of detected options.
+     *
+     * <p>Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[]) default argument value}
+     * for this option will cause this method to return that default value even if this option was not detected on the
+     * command line, or if this option can take an optional argument but did not have one on the command line.</p>
+     *
+     * @param detectedOptions the detected options to search in
+     * @return the arguments associated with this option; an empty list if no such arguments are present, or if this
+     * option was not detected
+     * @throws OptionException if there is a problem converting this option's arguments to the desired type; for
+     * example, if the type does not implement a correct conversion constructor or method
+     * @throws NullPointerException if {@code detectedOptions} is {@code null}
+     * @see OptionSet#valuesOf(OptionSpec)
+     */
+    List<V> values( OptionSet detectedOptions );
+
+    /**
+     * Gives the argument associated with the given option in the given set of detected options.
+     *
+     * <p>Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[]) default argument value}
+     * for this option will cause this method to return that default value even if this option was not detected on the
+     * command line, or if this option can take an optional argument but did not have one on the command line.</p>
+     *
+     * @param detectedOptions the detected options to search in
+     * @return the argument of the this option; {@code null} if no argument is present, or that option was not detected
+     * @throws OptionException if more than one argument was detected for the option
+     * @throws NullPointerException if {@code detectedOptions} is {@code null}
+     * @throws ClassCastException if the arguments of this option are not of the expected type
+     * @see OptionSet#valueOf(OptionSpec)
+     */
+    V value( OptionSet detectedOptions );
+
+    /**
+     * @return the string representations of this option
+     */
+    Collection<String> options();
+
+    /**
+     * Tells whether this option is designated as a "help" option. The presence of a "help" option on a command line
+     * means that missing "required" options will not cause parsing to fail.
+     *
+     * @return whether this option is designated as a "help" option
+     */
+    boolean isForHelp();
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionSpecBuilder.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionSpecBuilder.java
new file mode 100644
index 00000000000..4f171091ec0
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionSpecBuilder.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Allows callers to specify whether a given option accepts arguments (required or optional).
+ *
+ * <p>Instances are returned from {@link OptionParser#accepts(String)} to allow the formation of parser directives as
+ * sentences in a "fluent interface" language.  For example:</p>
+ *
+ * <pre><code>
+ *   OptionParser parser = new OptionParser();
+ *   parser.accepts( "c" ).<strong>withRequiredArg()</strong>.ofType( Integer.class );
+ * </code></pre>
+ *
+ * <p>If no methods are invoked on an instance of this class, then that instance's option will accept no argument.</p>
+ *
+ * <p>Note that you should not use the fluent interface clauses in a way that would defeat the typing of option
+ * arguments:</p>
+ *
+ * <pre><code>
+ *   OptionParser parser = new OptionParser();
+ *   ArgumentAcceptingOptionSpec&lt;String&gt; optionC =
+ *       parser.accepts( "c" ).withRequiredArg();
+ *   <strong>optionC.ofType( Integer.class );  // DON'T THROW AWAY THE TYPE!</strong>
+ *
+ *   String value = parser.parse( "-c", "2" ).valueOf( optionC );  // ClassCastException
+ * </code></pre>
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class OptionSpecBuilder extends NoArgumentOptionSpec {
+    private final OptionParser parser;
+
+    OptionSpecBuilder( OptionParser parser, Collection<String> options, String description ) {
+        super( options, description );
+
+        this.parser = parser;
+        attachToParser();
+    }
+
+    private void attachToParser() {
+        parser.recognize( this );
+    }
+
+    /**
+     * Informs an option parser that this builder's option requires an argument.
+     *
+     * @return a specification for the option
+     */
+    public ArgumentAcceptingOptionSpec<String> withRequiredArg() {
+        ArgumentAcceptingOptionSpec<String> newSpec =
+            new RequiredArgumentOptionSpec<String>( options(), description() );
+        parser.recognize( newSpec );
+
+        return newSpec;
+    }
+
+    /**
+     * Informs an option parser that this builder's option accepts an optional argument.
+     *
+     * @return a specification for the option
+     */
+    public ArgumentAcceptingOptionSpec<String> withOptionalArg() {
+        ArgumentAcceptingOptionSpec<String> newSpec =
+            new OptionalArgumentOptionSpec<String>( options(), description() );
+        parser.recognize( newSpec );
+
+        return newSpec;
+    }
+
+    /**
+     * <p>Informs an option parser that this builder's option is required if the given option is present on the command
+     * line.</p>
+     *
+     * <p>For a given option, you <em>should not</em> mix this with {@link #requiredUnless(String, String...)
+     * requiredUnless} to avoid conflicts.</p>
+     *
+     * @param dependent an option whose presence on a command line makes this builder's option required
+     * @param otherDependents other options whose presence on a command line makes this builder's option required
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     * @throws OptionException if any of the dependent options haven't been configured in the parser yet
+     */
+    public OptionSpecBuilder requiredIf( String dependent, String... otherDependents ) {
+        List<String> dependents = validatedDependents( dependent, otherDependents );
+        for ( String each : dependents ) {
+            parser.requiredIf( options(), each );
+        }
+
+        return this;
+    }
+
+    /**
+     * <p>Informs an option parser that this builder's option is required if the given option is present on the command
+     * line.</p>
+     *
+     * <p>For a given option, you <em>should not</em> mix this with {@link #requiredUnless(OptionSpec, OptionSpec[])
+     * requiredUnless} to avoid conflicts.</p>
+     *
+     * <p>This method recognizes only instances of options returned from the fluent interface methods.</p>
+     *
+     * @param dependent the option whose presence on a command line makes this builder's option required
+     * @param otherDependents other options whose presence on a command line makes this builder's option required
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     */
+    public OptionSpecBuilder requiredIf( OptionSpec<?> dependent, OptionSpec<?>... otherDependents ) {
+        parser.requiredIf( options(), dependent );
+        for ( OptionSpec<?> each : otherDependents )
+            parser.requiredIf( options(), each );
+
+        return this;
+    }
+
+    /**
+     * <p>Informs an option parser that this builder's option is required if the given option is absent on the command
+     * line.</p>
+     *
+     * <p>For a given option, you <em>should not</em> mix this with {@link #requiredIf(OptionSpec, OptionSpec[])
+     * requiredIf} to avoid conflicts.</p>
+     *
+     * @param dependent an option whose absence on a command line makes this builder's option required
+     * @param otherDependents other options whose absence on a command line makes this builder's option required
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     * @throws OptionException if any of the dependent options haven't been configured in the parser yet
+     */
+    public OptionSpecBuilder requiredUnless( String dependent, String... otherDependents ) {
+        List<String> dependents = validatedDependents( dependent, otherDependents );
+        for ( String each : dependents ) {
+            parser.requiredUnless( options(), each );
+        }
+        return this;
+    }
+
+    /**
+     * <p>Informs an option parser that this builder's option is required if the given option is absent on the command
+     * line.</p>
+     *
+     * <p>For a given option, you <em>should not</em> mix this with {@link #requiredIf(OptionSpec, OptionSpec[])
+     * requiredIf} to avoid conflicts.</p>
+     *
+     * <p>This method recognizes only instances of options returned from the fluent interface methods.</p>
+     *
+     * @param dependent the option whose absence on a command line makes this builder's option required
+     * @param otherDependents other options whose absence on a command line makes this builder's option required
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     */
+    public OptionSpecBuilder requiredUnless( OptionSpec<?> dependent, OptionSpec<?>... otherDependents ) {
+        parser.requiredUnless( options(), dependent );
+        for ( OptionSpec<?> each : otherDependents )
+            parser.requiredUnless( options(), each );
+
+        return this;
+    }
+
+    private List<String> validatedDependents( String dependent, String... otherDependents ) {
+        List<String> dependents = new ArrayList<String>();
+        dependents.add( dependent );
+        Collections.addAll( dependents, otherDependents );
+
+        for ( String each : dependents ) {
+            if ( !parser.isRecognized( each ) )
+                throw new UnconfiguredOptionException( each );
+        }
+
+        return dependents;
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionSpecTokenizer.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionSpecTokenizer.java
new file mode 100644
index 00000000000..d878daba6eb
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionSpecTokenizer.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple;
+
+import java.util.NoSuchElementException;
+
+import static jdk.internal.joptsimple.ParserRules.*;
+
+/**
+ * Tokenizes a short option specification string.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class OptionSpecTokenizer {
+    private static final char POSIXLY_CORRECT_MARKER = '+';
+    private static final char HELP_MARKER = '*';
+
+    private String specification;
+    private int index;
+
+    OptionSpecTokenizer( String specification ) {
+        if ( specification == null )
+            throw new NullPointerException( "null option specification" );
+
+        this.specification = specification;
+    }
+
+    boolean hasMore() {
+        return index < specification.length();
+    }
+
+    AbstractOptionSpec<?> next() {
+        if ( !hasMore() )
+            throw new NoSuchElementException();
+
+
+        String optionCandidate = String.valueOf( specification.charAt( index ) );
+        index++;
+
+        AbstractOptionSpec<?> spec;
+        if ( RESERVED_FOR_EXTENSIONS.equals( optionCandidate ) ) {
+            spec = handleReservedForExtensionsToken();
+
+            if ( spec != null )
+                return spec;
+        }
+
+        ensureLegalOption( optionCandidate );
+
+        if ( hasMore() ) {
+            boolean forHelp = false;
+            if ( specification.charAt( index ) == HELP_MARKER ) {
+                forHelp = true;
+                ++index;
+            }
+            spec = hasMore() && specification.charAt( index ) == ':'
+                ? handleArgumentAcceptingOption( optionCandidate )
+                : new NoArgumentOptionSpec( optionCandidate );
+            if ( forHelp )
+                spec.forHelp();
+        } else
+            spec = new NoArgumentOptionSpec( optionCandidate );
+
+        return spec;
+    }
+
+    void configure( OptionParser parser ) {
+        adjustForPosixlyCorrect( parser );
+
+        while ( hasMore() )
+            parser.recognize( next() );
+    }
+
+    private void adjustForPosixlyCorrect( OptionParser parser ) {
+        if ( POSIXLY_CORRECT_MARKER == specification.charAt( 0 ) ) {
+            parser.posixlyCorrect( true );
+            specification = specification.substring( 1 );
+        }
+    }
+
+    private AbstractOptionSpec<?> handleReservedForExtensionsToken() {
+        if ( !hasMore() )
+            return new NoArgumentOptionSpec( RESERVED_FOR_EXTENSIONS );
+
+        if ( specification.charAt( index ) == ';' ) {
+            ++index;
+            return new AlternativeLongOptionSpec();
+        }
+
+        return null;
+    }
+
+    private AbstractOptionSpec<?> handleArgumentAcceptingOption( String candidate ) {
+        index++;
+
+        if ( hasMore() && specification.charAt( index ) == ':' ) {
+            index++;
+            return new OptionalArgumentOptionSpec<String>( candidate );
+        }
+
+        return new RequiredArgumentOptionSpec<String>( candidate );
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionalArgumentOptionSpec.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionalArgumentOptionSpec.java
new file mode 100644
index 00000000000..894e00e3a29
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/OptionalArgumentOptionSpec.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple;
+
+import java.util.Collection;
+
+/**
+ * Specification of an option that accepts an optional argument.
+ *
+ * @param <V> represents the type of the arguments this option accepts
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class OptionalArgumentOptionSpec<V> extends ArgumentAcceptingOptionSpec<V> {
+    OptionalArgumentOptionSpec( String option ) {
+        super( option, false );
+    }
+
+    OptionalArgumentOptionSpec( Collection<String> options, String description ) {
+        super( options, false, description );
+    }
+
+    @Override
+    protected void detectOptionArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions ) {
+        if ( arguments.hasMore() ) {
+            String nextArgument = arguments.peek();
+
+            if ( !parser.looksLikeAnOption( nextArgument ) )
+                handleOptionArgument( parser, detectedOptions, arguments );
+            else if ( isArgumentOfNumberType() && canConvertArgument( nextArgument ) )
+                addArguments( detectedOptions, arguments.next() );
+            else
+                detectedOptions.add( this );
+        }
+        else
+            detectedOptions.add( this );
+    }
+
+    private void handleOptionArgument( OptionParser parser, OptionSet detectedOptions, ArgumentList arguments ) {
+        if ( parser.posixlyCorrect() ) {
+            detectedOptions.add( this );
+            parser.noMoreOptions();
+        }
+        else
+            addArguments( detectedOptions, arguments.next() );
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ParserRules.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ParserRules.java
new file mode 100644
index 00000000000..e981d878adc
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ParserRules.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple;
+
+import java.util.Collection;
+
+import static java.lang.Character.*;
+
+/**
+ * Can tell whether or not options are well-formed.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+final class ParserRules {
+    static final char HYPHEN_CHAR = '-';
+    static final String HYPHEN = String.valueOf( HYPHEN_CHAR );
+    static final String DOUBLE_HYPHEN = "--";
+    static final String OPTION_TERMINATOR = DOUBLE_HYPHEN;
+    static final String RESERVED_FOR_EXTENSIONS = "W";
+
+    private ParserRules() {
+        throw new UnsupportedOperationException();
+    }
+
+    static boolean isShortOptionToken( String argument ) {
+        return argument.startsWith( HYPHEN )
+            && !HYPHEN.equals( argument )
+            && !isLongOptionToken( argument );
+    }
+
+    static boolean isLongOptionToken( String argument ) {
+        return argument.startsWith( DOUBLE_HYPHEN ) && !isOptionTerminator( argument );
+    }
+
+    static boolean isOptionTerminator( String argument ) {
+        return OPTION_TERMINATOR.equals( argument );
+    }
+
+    static void ensureLegalOption( String option ) {
+        if ( option.startsWith( HYPHEN ) )
+            throw new IllegalOptionSpecificationException( String.valueOf( option ) );
+
+        for ( int i = 0; i < option.length(); ++i )
+            ensureLegalOptionCharacter( option.charAt( i ) );
+    }
+
+    static void ensureLegalOptions( Collection<String> options ) {
+        for ( String each : options )
+            ensureLegalOption( each );
+    }
+
+    private static void ensureLegalOptionCharacter( char option ) {
+        if ( !( isLetterOrDigit( option ) || isAllowedPunctuation( option ) ) )
+            throw new IllegalOptionSpecificationException( String.valueOf( option ) );
+    }
+
+    private static boolean isAllowedPunctuation( char option ) {
+        String allowedPunctuation = "?." + HYPHEN_CHAR;
+        return allowedPunctuation.indexOf( option ) != -1;
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/README b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/README
new file mode 100644
index 00000000000..ad0d0f64db5
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/README
@@ -0,0 +1,3 @@
+JOpt Simple, Version 4.6
+https://pholser.github.io/jopt-simple/
+
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/RequiredArgumentOptionSpec.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/RequiredArgumentOptionSpec.java
new file mode 100644
index 00000000000..9972f8d1cff
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/RequiredArgumentOptionSpec.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple;
+
+import java.util.Collection;
+
+/**
+ * Specification of an option that accepts a required argument.
+ *
+ * @param <V> represents the type of the arguments this option accepts
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class RequiredArgumentOptionSpec<V> extends ArgumentAcceptingOptionSpec<V> {
+    RequiredArgumentOptionSpec( String option ) {
+        super( option, true );
+    }
+
+    RequiredArgumentOptionSpec( Collection<String> options, String description ) {
+        super( options, true, description );
+    }
+
+    @Override
+    protected void detectOptionArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions ) {
+        if ( !arguments.hasMore() )
+            throw new OptionMissingRequiredArgumentException( options() );
+
+        addArguments( detectedOptions, arguments.next() );
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/UnacceptableNumberOfNonOptionsException.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/UnacceptableNumberOfNonOptionsException.java
new file mode 100644
index 00000000000..c5775fb3458
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/UnacceptableNumberOfNonOptionsException.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple;
+
+import static java.util.Collections.*;
+
+/**
+ * Thrown when the option parser detects an unacceptable number of {@code linkplain NonOptionArgumentSpec
+ * non-option arguments}.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class UnacceptableNumberOfNonOptionsException extends OptionException {
+    private static final long serialVersionUID = -1L;
+    private final int minimum;
+    private final int maximum;
+    private final int actual;
+
+    UnacceptableNumberOfNonOptionsException( int minimum, int maximum, int actual ) {
+        super( singletonList( NonOptionArgumentSpec.NAME ) );
+
+        this.minimum = minimum;
+        this.maximum = maximum;
+        this.actual = actual;
+    }
+
+    @Override
+    public String getMessage() {
+        return String.format( "actual = %d, minimum = %d, maximum = %d", actual, minimum, maximum );
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/UnconfiguredOptionException.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/UnconfiguredOptionException.java
new file mode 100644
index 00000000000..0783b7a684b
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/UnconfiguredOptionException.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple;
+
+import java.util.Collection;
+
+import static java.util.Collections.*;
+
+/**
+ * Thrown when an option parser refers to an option that is not in fact configured already on the parser.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class UnconfiguredOptionException extends OptionException {
+    private static final long serialVersionUID = -1L;
+
+    UnconfiguredOptionException( String option ) {
+        this( singletonList( option ) );
+    }
+
+    UnconfiguredOptionException( Collection<String> options ) {
+        super( options );
+    }
+
+    @Override
+    public String getMessage() {
+        return "Option " + multipleOptionMessage() + " has not been configured on this parser";
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/UnrecognizedOptionException.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/UnrecognizedOptionException.java
new file mode 100644
index 00000000000..b9ec16abb0a
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/UnrecognizedOptionException.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple;
+
+import static java.util.Collections.*;
+
+/**
+ * Thrown when the option parser encounters an unrecognized option.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class UnrecognizedOptionException extends OptionException {
+    private static final long serialVersionUID = -1L;
+
+    UnrecognizedOptionException( String option ) {
+        super( singletonList( option ) );
+    }
+
+    @Override
+    public String getMessage() {
+        return singleOptionMessage() + " is not a recognized option";
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ValueConversionException.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ValueConversionException.java
new file mode 100644
index 00000000000..3ea4938d21c
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ValueConversionException.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple;
+
+/**
+ * Thrown by {@link ValueConverter}s when problems occur in converting string values to other Java types.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class ValueConversionException extends RuntimeException {
+    private static final long serialVersionUID = -1L;
+
+    /**
+     * Creates a new exception with the specified detail message.
+     *
+     * @param message the detail message
+     */
+    public ValueConversionException( String message ) {
+        this( message, null );
+    }
+
+    /**
+     * Creates a new exception with the specified detail message and cause.
+     *
+     * @param message the detail message
+     * @param cause the original exception
+     */
+    public ValueConversionException( String message, Throwable cause ) {
+        super( message, cause );
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ValueConverter.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ValueConverter.java
new file mode 100644
index 00000000000..db08b2550d1
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/ValueConverter.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple;
+
+/**
+ * Instances of this interface are used to convert arguments of options into specific Java types.
+ *
+ * @param <V> constraint on the type of values being converted to
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public interface ValueConverter<V> {
+    /**
+     * Converts the given string value into a Java type.
+     *
+     * @param value the string to convert
+     * @return the converted value
+     * @throws ValueConversionException if a problem occurs while converting the value
+     */
+    V convert( String value );
+
+    /**
+     * Gives the class of the type of values this converter converts to.
+     *
+     * @return the target class for conversion
+     */
+    Class<V> valueType();
+
+    /**
+     * Gives a string that describes the pattern of the values this converter expects, if any.  For example, a date
+     * converter can respond with a {@link java.text.SimpleDateFormat date format string}.
+     *
+     * @return a value pattern, or {@code null} if there's nothing interesting here
+     */
+    String valuePattern();
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/AbbreviationMap.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/AbbreviationMap.java
new file mode 100644
index 00000000000..2646b7372b7
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/AbbreviationMap.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple.internal;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * <p>A map whose keys are strings; when a key/value pair is added to the map, the longest unique abbreviations of that
+ * key are added as well, and associated with the value. Thus:</p>
+ *
+ * <pre>
+ *   <code>
+ *   abbreviations.put( "good", "bye" );
+ *   </code>
+ * </pre>
+ *
+ * <p>would make it such that you could retrieve the value {@code "bye"} from the map using the keys {@code "good"},
+ * {@code "goo"}, {@code "go"}, and {@code "g"}. A subsequent invocation of:</p>
+ * <pre>
+ *   <code>
+ *   abbreviations.put( "go", "fish" );
+ *   </code>
+ * </pre>
+ *
+ * <p>would make it such that you could retrieve the value {@code "bye"} using the keys {@code "good"} and
+ * {@code "goo"}, and the value {@code "fish"} using the key {@code "go"}.  The key {@code "g"} would yield
+ * {@code null}, since it would no longer be a unique abbreviation.</p>
+ *
+ * <p>The data structure is much like a "trie".</p>
+ *
+ * @param <V> a constraint on the types of the values in the map
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ * @see <a href="http://www.perldoc.com/perl5.8.0/lib/Text/Abbrev.html">Perl's Text::Abbrev module</a>
+ */
+public class AbbreviationMap<V> {
+    private String key;
+    private V value;
+    private final Map<Character, AbbreviationMap<V>> children = new TreeMap<Character, AbbreviationMap<V>>();
+    private int keysBeyond;
+
+    /**
+     * <p>Tells whether the given key is in the map, or whether the given key is a unique
+     * abbreviation of a key that is in the map.</p>
+     *
+     * @param aKey key to look up
+     * @return {@code true} if {@code key} is present in the map
+     * @throws NullPointerException if {@code key} is {@code null}
+     */
+    public boolean contains( String aKey ) {
+        return get( aKey ) != null;
+    }
+
+    /**
+     * <p>Answers the value associated with the given key.  The key can be a unique
+     * abbreviation of a key that is in the map. </p>
+     *
+     * @param aKey key to look up
+     * @return the value associated with {@code aKey}; or {@code null} if there is no
+     * such value or {@code aKey} is not a unique abbreviation of a key in the map
+     * @throws NullPointerException if {@code aKey} is {@code null}
+     */
+    public V get( String aKey ) {
+        char[] chars = charsOf( aKey );
+
+        AbbreviationMap<V> child = this;
+        for ( char each : chars ) {
+            child = child.children.get( each );
+            if ( child == null )
+                return null;
+        }
+
+        return child.value;
+    }
+
+    /**
+     * <p>Associates a given value with a given key.  If there was a previous
+     * association, the old value is replaced with the new one.</p>
+     *
+     * @param aKey key to create in the map
+     * @param newValue value to associate with the key
+     * @throws NullPointerException if {@code aKey} or {@code newValue} is {@code null}
+     * @throws IllegalArgumentException if {@code aKey} is a zero-length string
+     */
+    public void put( String aKey, V newValue ) {
+        if ( newValue == null )
+            throw new NullPointerException();
+        if ( aKey.length() == 0 )
+            throw new IllegalArgumentException();
+
+        char[] chars = charsOf( aKey );
+        add( chars, newValue, 0, chars.length );
+    }
+
+    /**
+     * <p>Associates a given value with a given set of keys.  If there was a previous
+     * association, the old value is replaced with the new one.</p>
+     *
+     * @param keys keys to create in the map
+     * @param newValue value to associate with the key
+     * @throws NullPointerException if {@code keys} or {@code newValue} is {@code null}
+     * @throws IllegalArgumentException if any of {@code keys} is a zero-length string
+     */
+    public void putAll( Iterable<String> keys, V newValue ) {
+        for ( String each : keys )
+            put( each, newValue );
+    }
+
+    private boolean add( char[] chars, V newValue, int offset, int length ) {
+        if ( offset == length ) {
+            value = newValue;
+            boolean wasAlreadyAKey = key != null;
+            key = new String( chars );
+            return !wasAlreadyAKey;
+        }
+
+        char nextChar = chars[ offset ];
+        AbbreviationMap<V> child = children.get( nextChar );
+        if ( child == null ) {
+            child = new AbbreviationMap<V>();
+            children.put( nextChar, child );
+        }
+
+        boolean newKeyAdded = child.add( chars, newValue, offset + 1, length );
+
+        if ( newKeyAdded )
+            ++keysBeyond;
+
+        if ( key == null )
+            value = keysBeyond > 1 ? null : newValue;
+
+        return newKeyAdded;
+    }
+
+    /**
+     * <p>If the map contains the given key, dissociates the key from its value.</p>
+     *
+     * @param aKey key to remove
+     * @throws NullPointerException if {@code aKey} is {@code null}
+     * @throws IllegalArgumentException if {@code aKey} is a zero-length string
+     */
+    public void remove( String aKey ) {
+        if ( aKey.length() == 0 )
+            throw new IllegalArgumentException();
+
+        char[] keyChars = charsOf( aKey );
+        remove( keyChars, 0, keyChars.length );
+    }
+
+    private boolean remove( char[] aKey, int offset, int length ) {
+        if ( offset == length )
+            return removeAtEndOfKey();
+
+        char nextChar = aKey[ offset ];
+        AbbreviationMap<V> child = children.get( nextChar );
+        if ( child == null || !child.remove( aKey, offset + 1, length ) )
+            return false;
+
+        --keysBeyond;
+        if ( child.keysBeyond == 0 )
+            children.remove( nextChar );
+        if ( keysBeyond == 1 && key == null )
+            setValueToThatOfOnlyChild();
+
+        return true;
+    }
+
+    private void setValueToThatOfOnlyChild() {
+        Map.Entry<Character, AbbreviationMap<V>> entry = children.entrySet().iterator().next();
+        AbbreviationMap<V> onlyChild = entry.getValue();
+        value = onlyChild.value;
+    }
+
+    private boolean removeAtEndOfKey() {
+        if ( key == null )
+            return false;
+
+        key = null;
+        if ( keysBeyond == 1 )
+            setValueToThatOfOnlyChild();
+        else
+            value = null;
+
+        return true;
+    }
+
+    /**
+     * Gives a Java map representation of this abbreviation map.
+     *
+     * @return a Java map corresponding to this abbreviation map
+     */
+    public Map<String, V> toJavaUtilMap() {
+        Map<String, V> mappings = new TreeMap<String, V>();
+        addToMappings( mappings );
+        return mappings;
+    }
+
+    private void addToMappings( Map<String, V> mappings ) {
+        if ( key != null )
+            mappings.put( key, value );
+
+        for ( AbbreviationMap<V> each : children.values() )
+            each.addToMappings( mappings );
+    }
+
+    private static char[] charsOf( String aKey ) {
+        char[] chars = new char[ aKey.length() ];
+        aKey.getChars( 0, aKey.length(), chars, 0 );
+        return chars;
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Classes.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Classes.java
new file mode 100644
index 00000000000..f75025ccd19
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Classes.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple.internal;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public final class Classes {
+    private static final Map<Class<?>, Class<?>> WRAPPERS = new HashMap<Class<?>, Class<?>>( 13 );
+
+    static {
+        WRAPPERS.put( boolean.class, Boolean.class );
+        WRAPPERS.put( byte.class, Byte.class );
+        WRAPPERS.put( char.class, Character.class );
+        WRAPPERS.put( double.class, Double.class );
+        WRAPPERS.put( float.class, Float.class );
+        WRAPPERS.put( int.class, Integer.class );
+        WRAPPERS.put( long.class, Long.class );
+        WRAPPERS.put( short.class, Short.class );
+        WRAPPERS.put( void.class, Void.class );
+    }
+
+    private Classes() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Gives the "short version" of the given class name.  Somewhat naive to inner classes.
+     *
+     * @param className class name to chew on
+     * @return the short name of the class
+     */
+    public static String shortNameOf( String className ) {
+        return className.substring( className.lastIndexOf( '.' ) + 1 );
+    }
+
+    /**
+     * Gives the primitive wrapper class for the given class. If the given class is not
+     * {@linkplain Class#isPrimitive() primitive}, returns the class itself.
+     *
+     * @param <T> generic class type
+     * @param clazz the class to check
+     * @return primitive wrapper type if {@code clazz} is primitive, otherwise {@code clazz}
+     */
+    @SuppressWarnings( "unchecked" )
+    public static <T> Class<T> wrapperOf( Class<T> clazz ) {
+        return clazz.isPrimitive() ? (Class<T>) WRAPPERS.get( clazz ) : clazz;
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Columns.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Columns.java
new file mode 100644
index 00000000000..a3859f7d1cf
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Columns.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple.internal;
+
+import java.text.BreakIterator;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import static java.text.BreakIterator.*;
+
+import static jdk.internal.joptsimple.internal.Strings.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class Columns {
+    private static final int INDENT_WIDTH = 2;
+
+    private final int optionWidth;
+    private final int descriptionWidth;
+
+    Columns( int optionWidth, int descriptionWidth ) {
+        this.optionWidth = optionWidth;
+        this.descriptionWidth = descriptionWidth;
+    }
+
+    List<Row> fit( Row row ) {
+        List<String> options = piecesOf( row.option, optionWidth );
+        List<String> descriptions = piecesOf( row.description, descriptionWidth );
+
+        List<Row> rows = new ArrayList<Row>();
+        for ( int i = 0; i < Math.max( options.size(), descriptions.size() ); ++i )
+            rows.add( new Row( itemOrEmpty( options, i ), itemOrEmpty( descriptions, i ) ) );
+
+        return rows;
+    }
+
+    private static String itemOrEmpty( List<String> items, int index ) {
+        return index >= items.size() ? "" : items.get( index );
+    }
+
+    private List<String> piecesOf( String raw, int width ) {
+        List<String> pieces = new ArrayList<String>();
+
+        for ( String each : raw.trim().split( LINE_SEPARATOR ) )
+            pieces.addAll( piecesOfEmbeddedLine( each, width ) );
+
+        return pieces;
+    }
+
+    private List<String> piecesOfEmbeddedLine( String line, int width ) {
+        List<String> pieces = new ArrayList<String>();
+
+        BreakIterator words = BreakIterator.getLineInstance( Locale.US );
+        words.setText( line );
+
+        StringBuilder nextPiece = new StringBuilder();
+
+        int start = words.first();
+        for ( int end = words.next(); end != DONE; start = end, end = words.next() )
+            nextPiece = processNextWord( line, nextPiece, start, end, width, pieces );
+
+        if ( nextPiece.length() > 0 )
+            pieces.add( nextPiece.toString() );
+
+        return pieces;
+    }
+
+    private StringBuilder processNextWord( String source, StringBuilder nextPiece, int start, int end, int width,
+                                           List<String> pieces ) {
+        StringBuilder augmented = nextPiece;
+
+        String word = source.substring( start, end );
+        if ( augmented.length() + word.length() > width ) {
+            pieces.add( augmented.toString().replaceAll( "\\s+$", "" ) );
+            augmented = new StringBuilder( repeat( ' ', INDENT_WIDTH ) ).append( word );
+        }
+        else
+            augmented.append( word );
+
+        return augmented;
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/ConstructorInvokingValueConverter.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/ConstructorInvokingValueConverter.java
new file mode 100644
index 00000000000..720c6e4b8c2
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/ConstructorInvokingValueConverter.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple.internal;
+
+import java.lang.reflect.Constructor;
+
+import jdk.internal.joptsimple.ValueConverter;
+
+import static jdk.internal.joptsimple.internal.Reflection.*;
+
+/**
+ * @param <V> constraint on the type of values being converted to
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class ConstructorInvokingValueConverter<V> implements ValueConverter<V> {
+    private final Constructor<V> ctor;
+
+    ConstructorInvokingValueConverter( Constructor<V> ctor ) {
+        this.ctor = ctor;
+    }
+
+    public V convert( String value ) {
+        return instantiate( ctor, value );
+    }
+
+    public Class<V> valueType() {
+        return ctor.getDeclaringClass();
+    }
+
+    public String valuePattern() {
+        return null;
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/MethodInvokingValueConverter.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/MethodInvokingValueConverter.java
new file mode 100644
index 00000000000..599728bb67b
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/MethodInvokingValueConverter.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple.internal;
+
+import java.lang.reflect.Method;
+
+import jdk.internal.joptsimple.ValueConverter;
+
+import static jdk.internal.joptsimple.internal.Reflection.*;
+
+/**
+ * @param <V> constraint on the type of values being converted to
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class MethodInvokingValueConverter<V> implements ValueConverter<V> {
+    private final Method method;
+    private final Class<V> clazz;
+
+    MethodInvokingValueConverter( Method method, Class<V> clazz ) {
+        this.method = method;
+        this.clazz = clazz;
+    }
+
+    public V convert( String value ) {
+        return clazz.cast( invoke( method, value ) );
+    }
+
+    public Class<V> valueType() {
+        return clazz;
+    }
+
+    public String valuePattern() {
+        return null;
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Objects.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Objects.java
new file mode 100644
index 00000000000..2c2b696cd0a
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Objects.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple.internal;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public final class Objects {
+    private Objects() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Rejects {@code null} references.
+     *
+     * @param target reference to check
+     * @throws NullPointerException if {@code target} is {@code null}
+     */
+    public static void ensureNotNull( Object target ) {
+        if ( target == null )
+            throw new NullPointerException();
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Reflection.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Reflection.java
new file mode 100644
index 00000000000..240ad24394c
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Reflection.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple.internal;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import static java.lang.reflect.Modifier.*;
+
+import jdk.internal.joptsimple.ValueConverter;
+
+import static jdk.internal.joptsimple.internal.Classes.*;
+
+/**
+ * Helper methods for reflection.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public final class Reflection {
+    private Reflection() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Finds an appropriate value converter for the given class.
+     *
+     * @param <V> a constraint on the class object to introspect
+     * @param clazz class to introspect on
+     * @return a converter method or constructor
+     */
+    public static <V> ValueConverter<V> findConverter( Class<V> clazz ) {
+        Class<V> maybeWrapper = wrapperOf( clazz );
+
+        ValueConverter<V> valueOf = valueOfConverter( maybeWrapper );
+        if ( valueOf != null )
+            return valueOf;
+
+        ValueConverter<V> constructor = constructorConverter( maybeWrapper );
+        if ( constructor != null )
+            return constructor;
+
+        throw new IllegalArgumentException( clazz + " is not a value type" );
+    }
+
+    private static <V> ValueConverter<V> valueOfConverter( Class<V> clazz ) {
+        try {
+            Method valueOf = clazz.getDeclaredMethod( "valueOf", String.class );
+            if ( meetsConverterRequirements( valueOf, clazz ) )
+                return new MethodInvokingValueConverter<V>( valueOf, clazz );
+
+            return null;
+        }
+        catch ( NoSuchMethodException ignored ) {
+            return null;
+        }
+    }
+
+    private static <V> ValueConverter<V> constructorConverter( Class<V> clazz ) {
+        try {
+            return new ConstructorInvokingValueConverter<V>( clazz.getConstructor( String.class ) );
+        }
+        catch ( NoSuchMethodException ignored ) {
+            return null;
+        }
+    }
+
+    /**
+     * Invokes the given constructor with the given arguments.
+     *
+     * @param <T> constraint on the type of the objects yielded by the constructor
+     * @param constructor constructor to invoke
+     * @param args arguments to hand to the constructor
+     * @return the result of invoking the constructor
+     * @throws ReflectionException in lieu of the gaggle of reflection-related exceptions
+     */
+    public static <T> T instantiate( Constructor<T> constructor, Object... args ) {
+        try {
+            return constructor.newInstance( args );
+        }
+        catch ( Exception ex ) {
+            throw reflectionException( ex );
+        }
+    }
+
+    /**
+     * Invokes the given static method with the given arguments.
+     *
+     * @param method method to invoke
+     * @param args arguments to hand to the method
+     * @return the result of invoking the method
+     * @throws ReflectionException in lieu of the gaggle of reflection-related exceptions
+     */
+    public static Object invoke( Method method, Object... args ) {
+        try {
+            return method.invoke( null, args );
+        }
+        catch ( Exception ex ) {
+            throw reflectionException( ex );
+        }
+    }
+
+    @SuppressWarnings( "unchecked" )
+    public static <V> V convertWith( ValueConverter<V> converter, String raw ) {
+        return converter == null ? (V) raw : converter.convert( raw );
+    }
+
+    private static boolean meetsConverterRequirements( Method method, Class<?> expectedReturnType ) {
+        int modifiers = method.getModifiers();
+        return isPublic( modifiers ) && isStatic( modifiers ) && expectedReturnType.equals( method.getReturnType() );
+    }
+
+    private static RuntimeException reflectionException( Exception ex ) {
+        if ( ex instanceof IllegalArgumentException )
+            return new ReflectionException( ex );
+        if ( ex instanceof InvocationTargetException )
+            return new ReflectionException( ex.getCause() );
+        if ( ex instanceof RuntimeException )
+            return (RuntimeException) ex;
+
+        return new ReflectionException( ex );
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/ReflectionException.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/ReflectionException.java
new file mode 100644
index 00000000000..3e98d8f244c
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/ReflectionException.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple.internal;
+
+/**
+ * This unchecked exception wraps reflection-oriented exceptions.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class ReflectionException extends RuntimeException {
+    private static final long serialVersionUID = -2L;
+
+    ReflectionException( Throwable cause ) {
+        super( cause );
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Row.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Row.java
new file mode 100644
index 00000000000..0ce8bcf0cc9
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Row.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple.internal;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class Row {
+    final String option;
+    final String description;
+
+    Row( String option, String description ) {
+        this.option = option;
+        this.description = description;
+    }
+
+    @Override
+    public boolean equals( Object that ) {
+        if ( that == this )
+            return true;
+        if ( that == null || !getClass().equals( that.getClass() ) )
+            return false;
+
+        Row other = (Row) that;
+        return option.equals( other.option ) && description.equals( other.description );
+    }
+
+    @Override
+    public int hashCode() {
+        return option.hashCode() ^ description.hashCode();
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Rows.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Rows.java
new file mode 100644
index 00000000000..0d0e4c2d0e0
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Rows.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple.internal;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import static java.lang.Math.*;
+
+import static jdk.internal.joptsimple.internal.Strings.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class Rows {
+    private final int overallWidth;
+    private final int columnSeparatorWidth;
+    private final Set<Row> rows = new LinkedHashSet<Row>();
+    private int widthOfWidestOption;
+    private int widthOfWidestDescription;
+
+    public Rows( int overallWidth, int columnSeparatorWidth ) {
+        this.overallWidth = overallWidth;
+        this.columnSeparatorWidth = columnSeparatorWidth;
+    }
+
+    public void add( String option, String description ) {
+        add( new Row( option, description ) );
+    }
+
+    private void add( Row row ) {
+        rows.add( row );
+        widthOfWidestOption = max( widthOfWidestOption, row.option.length() );
+        widthOfWidestDescription = max( widthOfWidestDescription, row.description.length() );
+    }
+
+    private void reset() {
+        rows.clear();
+        widthOfWidestOption = 0;
+        widthOfWidestDescription = 0;
+    }
+
+    public void fitToWidth() {
+        Columns columns = new Columns( optionWidth(), descriptionWidth() );
+
+        Set<Row> fitted = new LinkedHashSet<Row>();
+        for ( Row each : rows )
+            fitted.addAll( columns.fit( each ) );
+
+        reset();
+
+        for ( Row each : fitted )
+            add( each );
+    }
+
+    public String render() {
+        StringBuilder buffer = new StringBuilder();
+
+        for ( Row each : rows ) {
+            pad( buffer, each.option, optionWidth() ).append( repeat( ' ', columnSeparatorWidth ) );
+            pad( buffer, each.description, descriptionWidth() ).append( LINE_SEPARATOR );
+        }
+
+        return buffer.toString();
+    }
+
+    private int optionWidth() {
+        return min( ( overallWidth - columnSeparatorWidth ) / 2, widthOfWidestOption );
+    }
+
+    private int descriptionWidth() {
+        return min( ( overallWidth - columnSeparatorWidth ) / 2, widthOfWidestDescription );
+    }
+
+    private StringBuilder pad( StringBuilder buffer, String s, int length ) {
+        buffer.append( s ).append( repeat( ' ', length - s.length() ) );
+        return buffer;
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Strings.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Strings.java
new file mode 100644
index 00000000000..038b3f0ce6b
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/internal/Strings.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple.internal;
+
+import java.util.Iterator;
+import java.util.List;
+
+import static java.lang.System.*;
+import static java.util.Arrays.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public final class Strings {
+    public static final String EMPTY = "";
+    public static final String SINGLE_QUOTE = "'";
+    public static final String LINE_SEPARATOR = getProperty( "line.separator" );
+
+    private Strings() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Gives a string consisting of the given character repeated the given number of times.
+     *
+     * @param ch the character to repeat
+     * @param count how many times to repeat the character
+     * @return the resultant string
+     */
+    public static String repeat( char ch, int count ) {
+        StringBuilder buffer = new StringBuilder();
+
+        for ( int i = 0; i < count; ++i )
+            buffer.append( ch );
+
+        return buffer.toString();
+    }
+
+    /**
+     * Tells whether the given string is either {@code} or consists solely of whitespace characters.
+     *
+     * @param target string to check
+     * @return {@code true} if the target string is null or empty
+     */
+    public static boolean isNullOrEmpty( String target ) {
+        return target == null || EMPTY.equals( target );
+    }
+
+
+    /**
+     * Gives a string consisting of a given string prepended and appended with surrounding characters.
+     *
+     * @param target a string
+     * @param begin character to prepend
+     * @param end character to append
+     * @return the surrounded string
+     */
+    public static String surround( String target, char begin, char end ) {
+        return begin + target + end;
+    }
+
+    /**
+     * Gives a string consisting of the elements of a given array of strings, each separated by a given separator
+     * string.
+     *
+     * @param pieces the strings to join
+     * @param separator the separator
+     * @return the joined string
+     */
+    public static String join( String[] pieces, String separator ) {
+        return join( asList( pieces ), separator );
+    }
+
+    /**
+     * Gives a string consisting of the string representations of the elements of a given array of objects,
+     * each separated by a given separator string.
+     *
+     * @param pieces the elements whose string representations are to be joined
+     * @param separator the separator
+     * @return the joined string
+     */
+    public static String join( List<String> pieces, String separator ) {
+        StringBuilder buffer = new StringBuilder();
+
+        for ( Iterator<String> iter = pieces.iterator(); iter.hasNext(); ) {
+            buffer.append( iter.next() );
+
+            if ( iter.hasNext() )
+                buffer.append( separator );
+        }
+
+        return buffer.toString();
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/util/DateConverter.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/util/DateConverter.java
new file mode 100644
index 00000000000..f90585300d1
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/util/DateConverter.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple.util;
+
+import java.text.DateFormat;
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import jdk.internal.joptsimple.ValueConversionException;
+import jdk.internal.joptsimple.ValueConverter;
+
+/**
+ * Converts values to {@link Date}s using a {@link DateFormat} object.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class DateConverter implements ValueConverter<Date> {
+    private final DateFormat formatter;
+
+    /**
+     * Creates a converter that uses the given date formatter/parser.
+     *
+     * @param formatter the formatter/parser to use
+     * @throws NullPointerException if {@code formatter} is {@code null}
+     */
+    public DateConverter( DateFormat formatter ) {
+        if ( formatter == null )
+            throw new NullPointerException( "illegal null formatter" );
+
+        this.formatter = formatter;
+    }
+
+    /**
+     * Creates a converter that uses a {@link SimpleDateFormat} with the given date/time pattern.  The date formatter
+     * created is not {@link SimpleDateFormat#setLenient(boolean) lenient}.
+     *
+     * @param pattern expected date/time pattern
+     * @return the new converter
+     * @throws NullPointerException if {@code pattern} is {@code null}
+     * @throws IllegalArgumentException if {@code pattern} is invalid
+     */
+    public static DateConverter datePattern( String pattern ) {
+        SimpleDateFormat formatter = new SimpleDateFormat( pattern );
+        formatter.setLenient( false );
+
+        return new DateConverter( formatter );
+    }
+
+    public Date convert( String value ) {
+        ParsePosition position = new ParsePosition( 0 );
+
+        Date date = formatter.parse( value, position );
+        if ( position.getIndex() != value.length() )
+            throw new ValueConversionException( message( value ) );
+
+        return date;
+    }
+
+    public Class<Date> valueType() {
+        return Date.class;
+    }
+
+    public String valuePattern() {
+        return formatter instanceof SimpleDateFormat
+            ? ( (SimpleDateFormat) formatter ).toPattern()
+            : "";
+    }
+
+    private String message( String value ) {
+        String message = "Value [" + value + "] does not match date/time pattern";
+        if ( formatter instanceof SimpleDateFormat )
+            message += " [" + ( (SimpleDateFormat) formatter ).toPattern() + ']';
+
+        return message;
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/util/InetAddressConverter.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/util/InetAddressConverter.java
new file mode 100644
index 00000000000..c6c7366509c
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/util/InetAddressConverter.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple.util;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import jdk.internal.joptsimple.ValueConversionException;
+import jdk.internal.joptsimple.ValueConverter;
+
+/**
+ * Converts values to {@link java.net.InetAddress} using {@link InetAddress#getByName(String) getByName}.
+ *
+ * @author <a href="mailto:r@ymund.de">Raymund F\u00FCl\u00F6p</a>
+ */
+public class InetAddressConverter implements ValueConverter<InetAddress> {
+    public InetAddress convert( String value ) {
+        try {
+            return InetAddress.getByName( value );
+        } catch ( UnknownHostException e ) {
+            throw new ValueConversionException( "Cannot convert value [" + value + " into an InetAddress", e );
+        }
+    }
+
+    public Class<InetAddress> valueType() {
+        return InetAddress.class;
+    }
+
+    public String valuePattern() {
+        return null;
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/util/KeyValuePair.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/util/KeyValuePair.java
new file mode 100644
index 00000000000..430e0c89b75
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/util/KeyValuePair.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple.util;
+
+import static jdk.internal.joptsimple.internal.Strings.*;
+
+/**
+ * <p>A simple string key/string value pair.</p>
+ *
+ * <p>This is useful as an argument type for options whose values take on the form <kbd>key=value</kbd>, such as JVM
+ * command line system properties.</p>
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public final class KeyValuePair {
+    public final String key;
+    public final String value;
+
+    private KeyValuePair( String key, String value ) {
+        this.key = key;
+        this.value = value;
+    }
+
+    /**
+     * Parses a string assumed to be of the form <kbd>key=value</kbd> into its parts.
+     *
+     * @param asString key-value string
+     * @return a key-value pair
+     * @throws NullPointerException if {@code stringRepresentation} is {@code null}
+     */
+    public static KeyValuePair valueOf( String asString ) {
+        int equalsIndex = asString.indexOf( '=' );
+        if ( equalsIndex == -1 )
+            return new KeyValuePair( asString, EMPTY );
+
+        String aKey = asString.substring( 0, equalsIndex );
+        String aValue = equalsIndex == asString.length() - 1 ? EMPTY : asString.substring( equalsIndex + 1 );
+
+        return new KeyValuePair( aKey, aValue );
+    }
+
+    @Override
+    public boolean equals( Object that ) {
+        if ( !( that instanceof KeyValuePair ) )
+            return false;
+
+        KeyValuePair other = (KeyValuePair) that;
+        return key.equals( other.key ) && value.equals( other.value );
+    }
+
+    @Override
+    public int hashCode() {
+        return key.hashCode() ^ value.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return key + '=' + value;
+    }
+}
diff --git a/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/util/RegexMatcher.java b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/util/RegexMatcher.java
new file mode 100644
index 00000000000..b6ceee5557e
--- /dev/null
+++ b/jdk/src/jdk.internal.opt/share/classes/jdk/internal/joptsimple/util/RegexMatcher.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2004-2014 Paul R. Holser, Jr.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package jdk.internal.joptsimple.util;
+
+import java.util.regex.Pattern;
+
+import static java.util.regex.Pattern.*;
+
+import jdk.internal.joptsimple.ValueConversionException;
+import jdk.internal.joptsimple.ValueConverter;
+
+/**
+ * Ensures that values entirely match a regular expression.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class RegexMatcher implements ValueConverter<String> {
+    private final Pattern pattern;
+
+    /**
+     * Creates a matcher that uses the given regular expression, modified by the given flags.
+     *
+     * @param pattern the regular expression pattern
+     * @param flags modifying regex flags
+     * @throws IllegalArgumentException if bit values other than those corresponding to the defined match flags are
+     * set in {@code flags}
+     * @throws java.util.regex.PatternSyntaxException if the expression's syntax is invalid
+     */
+    public RegexMatcher( String pattern, int flags ) {
+        this.pattern = compile( pattern, flags );
+    }
+
+    /**
+     * Gives a matcher that uses the given regular expression.
+     *
+     * @param pattern the regular expression pattern
+     * @return the new converter
+     * @throws java.util.regex.PatternSyntaxException if the expression's syntax is invalid
+     */
+    public static ValueConverter<String> regex( String pattern ) {
+        return new RegexMatcher( pattern, 0 );
+    }
+
+    public String convert( String value ) {
+        if ( !pattern.matcher( value ).matches() ) {
+            throw new ValueConversionException(
+                "Value [" + value + "] did not match regex [" + pattern.pattern() + ']' );
+        }
+
+        return value;
+    }
+
+    public Class<String> valueType() {
+        return String.class;
+    }
+
+    public String valuePattern() {
+        return pattern.pattern();
+    }
+}
diff --git a/jdk/src/jdk.policytool/share/classes/sun/security/tools/policytool/PolicyTool.java b/jdk/src/jdk.policytool/share/classes/sun/security/tools/policytool/PolicyTool.java
index 02a82d941f9..f8083997c09 100644
--- a/jdk/src/jdk.policytool/share/classes/sun/security/tools/policytool/PolicyTool.java
+++ b/jdk/src/jdk.policytool/share/classes/sun/security/tools/policytool/PolicyTool.java
@@ -65,8 +65,11 @@ import javax.swing.border.EmptyBorder;
  *
  * @see java.security.Policy
  * @since   1.2
+ * @deprecated The policytool tool has been deprecated and
+ * is planned to be removed in a future release.
  */
 
+@Deprecated
 public class PolicyTool {
 
     // for i18n
@@ -738,6 +741,8 @@ public class PolicyTool {
      * run the PolicyTool
      */
     public static void main(String args[]) {
+        System.out.println("Note: The policytool tool has been deprecated and" +
+                " is planned to be removed in a future release.\n");
         parseArgs(args);
         SwingUtilities.invokeLater(new Runnable() {
             public void run() {
@@ -873,6 +878,7 @@ public class PolicyTool {
  * The Permission contains the (Type, Name, Action) triplet.
  *
  */
+@SuppressWarnings("deprecation")
 class PolicyEntry {
 
     private CodeSource codesource;
@@ -1012,6 +1018,7 @@ class PolicyEntry {
 /**
  * The main window for the PolicyTool
  */
+@SuppressWarnings("deprecation")
 class ToolWindow extends JFrame {
     // use serialVersionUID from JDK 1.2.2 for interoperability
     private static final long serialVersionUID = 5682568601210376777L;
@@ -1549,6 +1556,7 @@ class ToolWindow extends JFrame {
 /**
  * General dialog window
  */
+@SuppressWarnings("deprecation")
 class ToolDialog extends JDialog {
     // use serialVersionUID from JDK 1.2.2 for interoperability
     private static final long serialVersionUID = -372244357011301190L;
@@ -2912,6 +2920,7 @@ class ToolDialog extends JDialog {
 /**
  * Event handler for the PolicyTool window
  */
+@SuppressWarnings("deprecation")
 class ToolWindowListener implements WindowListener {
 
     private PolicyTool tool;
@@ -2956,6 +2965,7 @@ class ToolWindowListener implements WindowListener {
 /**
  * Event handler for the Policy List
  */
+@SuppressWarnings("deprecation")
 class PolicyListListener extends MouseAdapter implements ActionListener {
 
     private PolicyTool tool;
@@ -2985,6 +2995,7 @@ class PolicyListListener extends MouseAdapter implements ActionListener {
 /**
  * Event handler for the File Menu
  */
+@SuppressWarnings("deprecation")
 class FileMenuListener implements ActionListener {
 
     private PolicyTool tool;
@@ -3083,6 +3094,7 @@ class FileMenuListener implements ActionListener {
 /**
  * Event handler for the main window buttons and Edit Menu
  */
+@SuppressWarnings("deprecation")
 class MainWindowListener implements ActionListener {
 
     private PolicyTool tool;
@@ -3158,6 +3170,7 @@ class MainWindowListener implements ActionListener {
  *    if edit is FALSE, then we are ADDing a new PolicyEntry,
  *    so we only need to update the GUI listing.
  */
+@SuppressWarnings("deprecation")
 class AddEntryDoneButtonListener implements ActionListener {
 
     private PolicyTool tool;
@@ -3224,6 +3237,7 @@ class AddEntryDoneButtonListener implements ActionListener {
 /**
  * Event handler for ChangeKeyStoreOKButton button
  */
+@SuppressWarnings("deprecation")
 class ChangeKeyStoreOKButtonListener implements ActionListener {
 
     private PolicyTool tool;
@@ -3270,6 +3284,7 @@ class ChangeKeyStoreOKButtonListener implements ActionListener {
 /**
  * Event handler for AddPrinButton button
  */
+@SuppressWarnings("deprecation")
 class AddPrinButtonListener implements ActionListener {
 
     private PolicyTool tool;
@@ -3295,6 +3310,7 @@ class AddPrinButtonListener implements ActionListener {
 /**
  * Event handler for AddPermButton button
  */
+@SuppressWarnings("deprecation")
 class AddPermButtonListener implements ActionListener {
 
     private PolicyTool tool;
@@ -3320,6 +3336,7 @@ class AddPermButtonListener implements ActionListener {
 /**
  * Event handler for AddPrinOKButton button
  */
+@SuppressWarnings("deprecation")
 class NewPolicyPrinOKButtonListener implements ActionListener {
 
     private PolicyTool tool;
@@ -3383,6 +3400,7 @@ class NewPolicyPrinOKButtonListener implements ActionListener {
 /**
  * Event handler for AddPermOKButton button
  */
+@SuppressWarnings("deprecation")
 class NewPolicyPermOKButtonListener implements ActionListener {
 
     private PolicyTool tool;
@@ -3446,6 +3464,7 @@ class NewPolicyPermOKButtonListener implements ActionListener {
 /**
  * Event handler for RemovePrinButton button
  */
+@SuppressWarnings("deprecation")
 class RemovePrinButtonListener implements ActionListener {
 
     private PolicyTool tool;
@@ -3481,6 +3500,7 @@ class RemovePrinButtonListener implements ActionListener {
 /**
  * Event handler for RemovePermButton button
  */
+@SuppressWarnings("deprecation")
 class RemovePermButtonListener implements ActionListener {
 
     private PolicyTool tool;
@@ -3523,6 +3543,7 @@ class RemovePermButtonListener implements ActionListener {
  * GUI listing.  If the user is editing an existing PolicyEntry, we
  * update both the GUI listing and the actual PolicyEntry.
  */
+@SuppressWarnings("deprecation")
 class EditPrinButtonListener extends MouseAdapter implements ActionListener {
 
     private PolicyTool tool;
@@ -3569,6 +3590,7 @@ class EditPrinButtonListener extends MouseAdapter implements ActionListener {
  * GUI listing.  If the user is editing an existing PolicyEntry, we
  * update both the GUI listing and the actual PolicyEntry.
  */
+@SuppressWarnings("deprecation")
 class EditPermButtonListener extends MouseAdapter implements ActionListener {
 
     private PolicyTool tool;
@@ -3609,6 +3631,7 @@ class EditPermButtonListener extends MouseAdapter implements ActionListener {
 /**
  * Event handler for Principal Popup Menu
  */
+@SuppressWarnings("deprecation")
 class PrincipalTypeMenuListener implements ItemListener {
 
     private ToolDialog td;
@@ -3660,6 +3683,7 @@ class PrincipalTypeMenuListener implements ItemListener {
 /**
  * Event handler for Permission Popup Menu
  */
+@SuppressWarnings("deprecation")
 class PermissionMenuListener implements ItemListener {
 
     private ToolDialog td;
@@ -3734,6 +3758,7 @@ class PermissionMenuListener implements ItemListener {
 /**
  * Event handler for Permission Name Popup Menu
  */
+@SuppressWarnings("deprecation")
 class PermissionNameMenuListener implements ItemListener {
 
     private ToolDialog td;
@@ -3887,6 +3912,7 @@ class StatusOKButtonListener implements ActionListener {
 /**
  * Event handler for UserSaveYes button
  */
+@SuppressWarnings("deprecation")
 class UserSaveYesButtonListener implements ActionListener {
 
     private ToolDialog us;
@@ -3941,6 +3967,7 @@ class UserSaveYesButtonListener implements ActionListener {
 /**
  * Event handler for UserSaveNoButton
  */
+@SuppressWarnings("deprecation")
 class UserSaveNoButtonListener implements ActionListener {
 
     private PolicyTool tool;
@@ -3989,6 +4016,7 @@ class UserSaveCancelButtonListener implements ActionListener {
 /**
  * Event handler for ConfirmRemovePolicyEntryOKButtonListener
  */
+@SuppressWarnings("deprecation")
 class ConfirmRemovePolicyEntryOKButtonListener implements ActionListener {
 
     private PolicyTool tool;
@@ -4144,6 +4172,7 @@ class AudioPerm extends Perm {
     }
 }
 
+@SuppressWarnings("deprecation")
 class AuthPerm extends Perm {
     AuthPerm() {
         super(javax.security.auth.AuthPermission.class,
@@ -4216,6 +4245,7 @@ class FilePerm extends Perm {
     }
 }
 
+@SuppressWarnings("deprecation")
 class URLPerm extends Perm {
     URLPerm() {
         super(java.net.URLPermission.class,
@@ -4380,6 +4410,7 @@ class ReflectPerm extends Perm {
     }
 }
 
+@SuppressWarnings("deprecation")
 class RuntimePerm extends Perm {
     RuntimePerm() {
         super(java.lang.RuntimePermission.class,
@@ -4420,6 +4451,7 @@ class RuntimePerm extends Perm {
     }
 }
 
+@SuppressWarnings("deprecation")
 class SecurityPerm extends Perm {
     SecurityPerm() {
         super(java.security.SecurityPermission.class,
diff --git a/jdk/test/TEST.groups b/jdk/test/TEST.groups
index fbe25e8224f..e608d9d98b5 100644
--- a/jdk/test/TEST.groups
+++ b/jdk/test/TEST.groups
@@ -77,6 +77,7 @@ jdk_lang = \
     sun/misc \
     sun/reflect \
     jdk/lambda \
+    jdk/internal/misc \
     vm
 
 # All of the java.util package
@@ -144,7 +145,8 @@ jdk_stream = \
     java/util/stream
 
 jdk_math = \
-    java/math
+    java/math \
+    jdk/internal/math
 
 jdk_io = \
     java/io
diff --git a/jdk/test/com/sun/jdi/LineNumberInfo.java b/jdk/test/com/sun/jdi/LineNumberInfo.java
index c5166da0c43..5b7ff743efa 100644
--- a/jdk/test/com/sun/jdi/LineNumberInfo.java
+++ b/jdk/test/com/sun/jdi/LineNumberInfo.java
@@ -29,7 +29,7 @@
  *
  *  @modules jdk.jdi
  *  @run build TestScaffold VMConnection TargetListener TargetAdapter
- *  @run compile -g LineNumberInfo.java ControlFlow.java
+ *  @run compile -XDstringConcat=inline -g LineNumberInfo.java ControlFlow.java
  *
  *  @run driver LineNumberInfo
  */
diff --git a/jdk/test/com/sun/jdi/sde/InstallSDE.java b/jdk/test/com/sun/jdi/sde/InstallSDE.java
index 81bb3052778..17f6558760d 100644
--- a/jdk/test/com/sun/jdi/sde/InstallSDE.java
+++ b/jdk/test/com/sun/jdi/sde/InstallSDE.java
@@ -253,12 +253,16 @@ class InstallSDE {
                 case 3:  // Integer
                 case 4:  // Float
                 case 12: // NameAndType
+                case 18: // InvokeDynamic
                     copy(4);
                     break;
                 case 5:  // Long
                 case 6:  // Double
                     copy(8);
                     break;
+                case 15: // MethodHandle
+                    copy(3);
+                    break;
                 case 1:  // Utf8
                     int len = readU2();
                     writeU2(len);
diff --git a/jdk/test/java/lang/String/concat/ImplicitStringConcat.java b/jdk/test/java/lang/String/concat/ImplicitStringConcat.java
new file mode 100644
index 00000000000..5eb78602498
--- /dev/null
+++ b/jdk/test/java/lang/String/concat/ImplicitStringConcat.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ * @summary test implicit String concatenations
+ *
+ * @compile ImplicitStringConcat.java
+ * @run main/othervm ImplicitStringConcat
+ *
+ * @compile -XDstringConcat=inline ImplicitStringConcat.java
+ * @run main/othervm ImplicitStringConcat
+ *
+ * @compile -XDstringConcat=indy -source 1.9 -target 1.9 ImplicitStringConcat.java
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              ImplicitStringConcat
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcat
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcat
+
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcat
+ *
+ * @compile -XDstringConcat=indyWithConstants -source 1.9 -target 1.9 ImplicitStringConcat.java
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              ImplicitStringConcat
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcat
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcat
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcat
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcat
+*/
+import java.lang.StringBuilder;
+
+public class ImplicitStringConcat {
+
+    static boolean b = true;
+    static byte by = 42;
+    static short sh = 42;
+    static char ch = 'a';
+    static int i = 42;
+    static float fl = 42.0f;
+    static long l = 42;
+    static double d = 42.0d;
+    static String s = "foo";
+    static String sNull = null;
+    static Object o = "bar";
+    static Object oNull = null;
+    static CharSequence cs = "bar";
+    static char[] chars = new char[] {'a'};
+
+    static MyClass myCl = new MyClass();
+    static MyClassNull myClNull = new MyClassNull();
+    static Object  myCl2 = new MyClass();
+    static Object[] myArr = new Object[] { myCl };
+    static final Object[] s_myArr = new Object[] { myCl };
+
+    static StringBuffer sb = new StringBuffer("a");
+
+    public static void main(String[] args) throws Exception {
+
+        test("footrue", s + b);
+        test("foo42",   s + by);
+        test("foo42",   s + sh);
+        test("fooa",    s + ch);
+        test("foo42",   s + i);
+        test("foo42",   s + l);
+        test("foo42.0", s + fl);
+        test("foo42.0", s + d);
+        test("foofoo",  s + s);
+        test("foonull", s + sNull);
+        test("foobar",  s + o);
+        test("foonull", s + oNull);
+        test("foobar",  s + cs);
+
+        {
+            StringBuilder sb = new StringBuilder();
+            sb.append("foo");
+            sb.append(myArr.toString());
+            test(sb.toString(), s + myArr);
+        }
+
+        {
+            StringBuilder sb = new StringBuilder();
+            sb.append("foo");
+            sb.append(s_myArr.toString());
+            test(sb.toString(), s + s_myArr);
+        }
+
+        {
+            StringBuilder sb = new StringBuilder();
+            sb.append("foo[C@");
+            sb.append(Integer.toHexString(System.identityHashCode(chars)));
+            test(sb.toString(), s + chars);
+        }
+
+        test("fooa",    s + ImplicitStringConcat.sb);
+        test("foonull", s + null);
+        test("fooMyClass", s + myCl);
+        test("foonull",    s + myClNull);
+        test("fooMyClass", s + myCl2);
+
+        s = "foo";  s += b;     test("footrue", s);
+        s = "foo";  s += by;    test("foo42", s);
+        s = "foo";  s += sh;    test("foo42", s);
+        s = "foo";  s += ch;    test("fooa", s);
+        s = "foo";  s += i;     test("foo42", s);
+        s = "foo";  s += l;     test("foo42", s);
+        s = "foo";  s += fl;    test("foo42.0", s);
+        s = "foo";  s += d;     test("foo42.0", s);
+        s = "foo";  s += s;     test("foofoo", s);
+        s = "foo";  s += sNull; test("foonull", s);
+        s = "foo";  s += o;     test("foobar", s);
+        s = "foo";  s += oNull; test("foonull", s);
+        s = "foo";  s += cs;    test("foobar", s);
+
+        {
+            StringBuilder sb = new StringBuilder();
+            sb.append("foo[C@");
+            sb.append(Integer.toHexString(System.identityHashCode(chars)));
+            s = "foo";
+            s += chars;
+            test(sb.toString(), s);
+        }
+
+        s = "foo";  s += ImplicitStringConcat.sb;    test("fooa", s);
+        s = "foo";  s += null;  test("foonull", s);
+        s = "foo";  s += myCl;  test("fooMyClass", s);
+        s = "foo";  s += myCl2; test("fooMyClass", s);
+    }
+
+    public static void test(String expected, String actual) {
+       // Fingers crossed: String concat should work.
+       if (!expected.equals(actual)) {
+           StringBuilder sb = new StringBuilder();
+           sb.append("Expected = ");
+           sb.append(expected);
+           sb.append(", actual = ");
+           sb.append(actual);
+           throw new IllegalStateException(sb.toString());
+       }
+    }
+
+    static class MyClass {
+        public String toString() {
+            return "MyClass";
+        }
+    }
+
+    static class MyClassNull {
+        public String toString() {
+            return null;
+        }
+    }
+}
diff --git a/jdk/test/java/lang/String/concat/ImplicitStringConcatArgCount.java b/jdk/test/java/lang/String/concat/ImplicitStringConcatArgCount.java
new file mode 100644
index 00000000000..0cc0a2a7887
--- /dev/null
+++ b/jdk/test/java/lang/String/concat/ImplicitStringConcatArgCount.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ * @summary Test multiple number of arguments to concatenate.
+ *
+ * @compile ImplicitStringConcatArgCount.java
+ * @run main/othervm ImplicitStringConcatArgCount
+ *
+ * @compile -XDallowStringFolding=false -XDstringConcat=inline ImplicitStringConcatArgCount.java
+ * @run main/othervm ImplicitStringConcatArgCount
+ *
+ * @compile -XDallowStringFolding=false -XDstringConcat=indy -source 1.9 -target 1.9 ImplicitStringConcatArgCount.java
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              ImplicitStringConcatArgCount
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatArgCount
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatArgCount
+
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatArgCount
+ *
+ * @compile -XDallowStringFolding=false -XDstringConcat=indyWithConstants -source 1.9 -target 1.9 ImplicitStringConcatArgCount.java
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              ImplicitStringConcatArgCount
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatArgCount
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatArgCount
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatArgCount
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatArgCount
+*/
+public class ImplicitStringConcatArgCount {
+    static final String s = "f";
+    static final String s1 = "o";
+    static String s2 = "o";
+    static int i = 7;
+
+    public static void main(String[] args) throws Exception {
+        test("fo",          s + s1);
+        test("foo",         s + s1 + s2);
+        test("foo7",        s + s1 + s2 + i);
+        test("foo77",       s + s1 + s2 + i + i);
+        test("foo777",      s + s1 + s2 + i + i + i);
+        test("foo7777",     s + s1 + s2 + i + i + i + i);
+        test("foo77777",    s + s1 + s2 + i + i + i + i + i);
+        test("foo777777",   s + s1 + s2 + i + i + i + i + i + i);
+        test("foo7777777",  s + s1 + s2 + i + i + i + i + i + i + i);
+        test("foo77777777", s + s1 + s2 + i + i + i + i + i + i + i + i);
+    }
+
+    public static void test(String expected, String actual) {
+       if (!expected.equals(actual)) {
+           StringBuilder sb = new StringBuilder();
+           sb.append("Expected = ");
+           sb.append(expected);
+           sb.append(", actual = ");
+           sb.append(actual);
+           throw new IllegalStateException(sb.toString());
+       }
+    }
+}
diff --git a/jdk/test/java/lang/String/concat/ImplicitStringConcatBoundaries.java b/jdk/test/java/lang/String/concat/ImplicitStringConcatBoundaries.java
new file mode 100644
index 00000000000..f8e677b12bb
--- /dev/null
+++ b/jdk/test/java/lang/String/concat/ImplicitStringConcatBoundaries.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ * @summary Test the boundary values for concatenation arguments.
+ *
+ * @compile ImplicitStringConcatBoundaries.java
+ * @run main/othervm ImplicitStringConcatBoundaries
+ *
+ * @compile -XDstringConcat=inline ImplicitStringConcatBoundaries.java
+ * @run main/othervm ImplicitStringConcatBoundaries
+ *
+ * @compile -XDstringConcat=indy -source 1.9 -target 1.9 ImplicitStringConcatBoundaries.java
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              ImplicitStringConcatBoundaries
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatBoundaries
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatBoundaries
+
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatBoundaries
+ *
+ * @compile -XDstringConcat=indyWithConstants -source 1.9 -target 1.9 ImplicitStringConcatBoundaries.java
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              ImplicitStringConcatBoundaries
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatBoundaries
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatBoundaries
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatBoundaries
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatBoundaries
+
+*/
+
+public class ImplicitStringConcatBoundaries {
+
+    public static final boolean BOOL_TRUE_1         = true;
+    public static       boolean BOOL_TRUE_2         = true;
+    public static final boolean BOOL_FALSE_1        = false;
+    public static       boolean BOOL_FALSE_2        = false;
+
+    public static final byte    BYTE_MIN_1          = Byte.MIN_VALUE;
+    public static       byte    BYTE_MIN_2          = Byte.MIN_VALUE;
+    public static final byte    BYTE_MAX_1          = Byte.MAX_VALUE;
+    public static       byte    BYTE_MAX_2          = Byte.MAX_VALUE;
+
+    public static final short   SHORT_MIN_1         = Short.MIN_VALUE;
+    public static       short   SHORT_MIN_2         = Short.MIN_VALUE;
+    public static final short   SHORT_MAX_1         = Short.MAX_VALUE;
+    public static       short   SHORT_MAX_2         = Short.MAX_VALUE;
+
+    public static final char    CHAR_MIN_1          = Character.MIN_VALUE;
+    public static       char    CHAR_MIN_2          = Character.MIN_VALUE;
+    public static final char    CHAR_MAX_1          = Character.MAX_VALUE;
+    public static       char    CHAR_MAX_2          = Character.MAX_VALUE;
+
+    public static final int     INT_MIN_1           = Integer.MIN_VALUE;
+    public static       int     INT_MIN_2           = Integer.MIN_VALUE;
+    public static final int     INT_MAX_1           = Integer.MAX_VALUE;
+    public static       int     INT_MAX_2           = Integer.MAX_VALUE;
+
+    public static final float   FLOAT_MIN_EXP_1     = Float.MIN_EXPONENT;
+    public static       float   FLOAT_MIN_EXP_2     = Float.MIN_EXPONENT;
+    public static final float   FLOAT_MIN_NORM_1    = Float.MIN_NORMAL;
+    public static       float   FLOAT_MIN_NORM_2    = Float.MIN_NORMAL;
+    public static final float   FLOAT_MIN_1         = Float.MIN_VALUE;
+    public static       float   FLOAT_MIN_2         = Float.MIN_VALUE;
+    public static final float   FLOAT_MAX_1         = Float.MAX_VALUE;
+    public static       float   FLOAT_MAX_2         = Float.MAX_VALUE;
+
+    public static final long    LONG_MIN_1          = Long.MIN_VALUE;
+    public static       long    LONG_MIN_2          = Long.MIN_VALUE;
+    public static final long    LONG_MAX_1          = Long.MAX_VALUE;
+    public static       long    LONG_MAX_2          = Long.MAX_VALUE;
+
+    public static final double  DOUBLE_MIN_EXP_1    = Double.MIN_EXPONENT;
+    public static       double  DOUBLE_MIN_EXP_2    = Double.MIN_EXPONENT;
+    public static final double  DOUBLE_MIN_NORM_1   = Double.MIN_NORMAL;
+    public static       double  DOUBLE_MIN_NORM_2   = Double.MIN_NORMAL;
+    public static final double  DOUBLE_MIN_1        = Double.MIN_VALUE;
+    public static       double  DOUBLE_MIN_2        = Double.MIN_VALUE;
+    public static final double  DOUBLE_MAX_1        = Double.MAX_VALUE;
+    public static       double  DOUBLE_MAX_2        = Double.MAX_VALUE;
+
+    public static void main(String[] args) throws Exception {
+        test("foofalse",                    "foo" + BOOL_FALSE_1);
+        test("foofalse",                    "foo" + BOOL_FALSE_2);
+        test("footrue",                     "foo" + BOOL_TRUE_1);
+        test("footrue",                     "foo" + BOOL_TRUE_2);
+
+        test("foo127",                      "foo" + BYTE_MAX_1);
+        test("foo127",                      "foo" + BYTE_MAX_2);
+        test("foo-128",                     "foo" + BYTE_MIN_1);
+        test("foo-128",                     "foo" + BYTE_MIN_2);
+
+        test("foo32767",                    "foo" + SHORT_MAX_1);
+        test("foo32767",                    "foo" + SHORT_MAX_2);
+        test("foo-32768",                   "foo" + SHORT_MIN_1);
+        test("foo-32768",                   "foo" + SHORT_MIN_2);
+
+        test("foo\u0000",                   "foo" + CHAR_MIN_1);
+        test("foo\u0000",                   "foo" + CHAR_MIN_2);
+        test("foo\uFFFF",                   "foo" + CHAR_MAX_1);
+        test("foo\uFFFF",                   "foo" + CHAR_MAX_2);
+
+        test("foo2147483647",               "foo" + INT_MAX_1);
+        test("foo2147483647",               "foo" + INT_MAX_2);
+        test("foo-2147483648",              "foo" + INT_MIN_1);
+        test("foo-2147483648",              "foo" + INT_MIN_2);
+
+        test("foo1.17549435E-38",           "foo" + FLOAT_MIN_NORM_1);
+        test("foo1.17549435E-38",           "foo" + FLOAT_MIN_NORM_2);
+        test("foo-126.0",                   "foo" + FLOAT_MIN_EXP_1);
+        test("foo-126.0",                   "foo" + FLOAT_MIN_EXP_2);
+        test("foo1.4E-45",                  "foo" + FLOAT_MIN_1);
+        test("foo1.4E-45",                  "foo" + FLOAT_MIN_2);
+        test("foo3.4028235E38",             "foo" + FLOAT_MAX_1);
+        test("foo3.4028235E38",             "foo" + FLOAT_MAX_2);
+
+        test("foo-9223372036854775808",     "foo" + LONG_MIN_1);
+        test("foo-9223372036854775808",     "foo" + LONG_MIN_2);
+        test("foo9223372036854775807",      "foo" + LONG_MAX_1);
+        test("foo9223372036854775807",      "foo" + LONG_MAX_2);
+
+        test("foo2.2250738585072014E-308",  "foo" + DOUBLE_MIN_NORM_1);
+        test("foo2.2250738585072014E-308",  "foo" + DOUBLE_MIN_NORM_2);
+        test("foo-1022.0",                  "foo" + DOUBLE_MIN_EXP_1);
+        test("foo-1022.0",                  "foo" + DOUBLE_MIN_EXP_2);
+        test("foo4.9E-324",                 "foo" + DOUBLE_MIN_1);
+        test("foo4.9E-324",                 "foo" + DOUBLE_MIN_2);
+        test("foo1.7976931348623157E308",   "foo" + DOUBLE_MAX_1);
+        test("foo1.7976931348623157E308",   "foo" + DOUBLE_MAX_2);
+    }
+
+    public static void test(String expected, String actual) {
+       if (!expected.equals(actual)) {
+           StringBuilder sb = new StringBuilder();
+           sb.append("Expected = ");
+           sb.append(expected);
+           sb.append(", actual = ");
+           sb.append(actual);
+           throw new IllegalStateException(sb.toString());
+       }
+    }
+}
diff --git a/jdk/test/java/lang/String/concat/ImplicitStringConcatMany.java b/jdk/test/java/lang/String/concat/ImplicitStringConcatMany.java
new file mode 100644
index 00000000000..7af7e7d5e05
--- /dev/null
+++ b/jdk/test/java/lang/String/concat/ImplicitStringConcatMany.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ * @summary Test implicit String concatenations with lots of arguments.
+ *
+ * @compile ImplicitStringConcatMany.java
+ * @run main/othervm ImplicitStringConcatMany
+ *
+ * @compile -XDstringConcat=inline ImplicitStringConcatMany.java
+ * @run main/othervm ImplicitStringConcatMany
+ *
+ * @compile -XDstringConcat=indy -source 1.9 -target 1.9 ImplicitStringConcatMany.java
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              ImplicitStringConcatMany
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatMany
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatMany
+
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatMany
+ *
+ * @compile -XDstringConcat=indyWithConstants -source 1.9 -target 1.9 ImplicitStringConcatMany.java
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              ImplicitStringConcatMany
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatMany
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatMany
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatMany
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatMany
+*/
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+public class ImplicitStringConcatMany {
+
+    static String s000, s001, s002, s003, s004, s005, s006, s007, s008, s009;
+    static String s010, s011, s012, s013, s014, s015, s016, s017, s018, s019;
+    static String s020, s021, s022, s023, s024, s025, s026, s027, s028, s029;
+    static String s030, s031, s032, s033, s034, s035, s036, s037, s038, s039;
+    static String s040, s041, s042, s043, s044, s045, s046, s047, s048, s049;
+    static String s050, s051, s052, s053, s054, s055, s056, s057, s058, s059;
+    static String s060, s061, s062, s063, s064, s065, s066, s067, s068, s069;
+    static String s070, s071, s072, s073, s074, s075, s076, s077, s078, s079;
+    static String s080, s081, s082, s083, s084, s085, s086, s087, s088, s089;
+    static String s090, s091, s092, s093, s094, s095, s096, s097, s098, s099;
+
+    static String s100, s101, s102, s103, s104, s105, s106, s107, s108, s109;
+    static String s110, s111, s112, s113, s114, s115, s116, s117, s118, s119;
+    static String s120, s121, s122, s123, s124, s125, s126, s127, s128, s129;
+    static String s130, s131, s132, s133, s134, s135, s136, s137, s138, s139;
+    static String s140, s141, s142, s143, s144, s145, s146, s147, s148, s149;
+    static String s150, s151, s152, s153, s154, s155, s156, s157, s158, s159;
+    static String s160, s161, s162, s163, s164, s165, s166, s167, s168, s169;
+    static String s170, s171, s172, s173, s174, s175, s176, s177, s178, s179;
+    static String s180, s181, s182, s183, s184, s185, s186, s187, s188, s189;
+    static String s190, s191, s192, s193, s194, s195, s196, s197, s198, s199;
+
+    static String s200, s201, s202, s203, s204, s205, s206, s207, s208, s209;
+    static String s210, s211, s212, s213, s214, s215, s216, s217, s218, s219;
+    static String s220, s221, s222, s223, s224, s225, s226, s227, s228, s229;
+    static String s230, s231, s232, s233, s234, s235, s236, s237, s238, s239;
+    static String s240, s241, s242, s243, s244, s245, s246, s247, s248, s249;
+    static String s250, s251, s252, s253, s254, s255, s256, s257, s258, s259;
+    static String s260, s261, s262, s263, s264, s265, s266, s267, s268, s269;
+    static String s270, s271, s272, s273, s274, s275, s276, s277, s278, s279;
+    static String s280, s281, s282, s283, s284, s285, s286, s287, s288, s289;
+    static String s290, s291, s292, s293, s294, s295, s296, s297, s298, s299;
+
+    static {
+        for (Field f : ImplicitStringConcatMany.class.getDeclaredFields()) {
+            if (Modifier.isStatic(f.getModifiers())) {
+                String name = f.getName();
+                try {
+                    f.set(null, name);
+                } catch (IllegalAccessException e) {
+                    throw new IllegalStateException(e);
+                }
+            }
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        String res =
+            s000 + s001 + s002 + s003 + s004 + s005 + s006 + s007 + s008 + s009 +
+            s010 + s011 + s012 + s013 + s014 + s015 + s016 + s017 + s018 + s019 +
+            s020 + s021 + s022 + s023 + s024 + s025 + s026 + s027 + s028 + s029 +
+            s030 + s031 + s032 + s033 + s034 + s035 + s036 + s037 + s038 + s039 +
+            s040 + s041 + s042 + s043 + s044 + s045 + s046 + s047 + s048 + s049 +
+            s050 + s051 + s052 + s053 + s054 + s055 + s056 + s057 + s058 + s059 +
+            s060 + s061 + s062 + s063 + s064 + s065 + s066 + s067 + s068 + s069 +
+            s070 + s071 + s072 + s073 + s074 + s075 + s076 + s077 + s078 + s079 +
+            s080 + s081 + s082 + s083 + s084 + s085 + s086 + s087 + s088 + s089 +
+            s090 + s091 + s092 + s093 + s094 + s095 + s096 + s097 + s098 + s099 +
+
+            s100 + s101 + s102 + s103 + s104 + s105 + s106 + s107 + s108 + s109 +
+            s110 + s111 + s112 + s113 + s114 + s115 + s116 + s117 + s118 + s119 +
+            s120 + s121 + s122 + s123 + s124 + s125 + s126 + s127 + s128 + s129 +
+            s130 + s131 + s132 + s133 + s134 + s135 + s136 + s137 + s138 + s139 +
+            s140 + s141 + s142 + s143 + s144 + s145 + s146 + s147 + s148 + s149 +
+            s150 + s151 + s152 + s153 + s154 + s155 + s156 + s157 + s158 + s159 +
+            s160 + s161 + s162 + s163 + s164 + s165 + s166 + s167 + s168 + s169 +
+            s170 + s171 + s172 + s173 + s174 + s175 + s176 + s177 + s178 + s179 +
+            s180 + s181 + s182 + s183 + s184 + s185 + s186 + s187 + s188 + s189 +
+            s190 + s191 + s192 + s193 + s194 + s195 + s196 + s197 + s198 + s199 +
+
+            s200 + s201 + s202 + s203 + s204 + s205 + s206 + s207 + s208 + s209 +
+            s210 + s211 + s212 + s213 + s214 + s215 + s216 + s217 + s218 + s219 +
+            s220 + s221 + s222 + s223 + s224 + s225 + s226 + s227 + s228 + s229 +
+            s230 + s231 + s232 + s233 + s234 + s235 + s236 + s237 + s238 + s239 +
+            s240 + s241 + s242 + s243 + s244 + s245 + s246 + s247 + s248 + s249 +
+            s250 + s251 + s252 + s253 + s254 + s255 + s256 + s257 + s258 + s259 +
+            s260 + s261 + s262 + s263 + s264 + s265 + s266 + s267 + s268 + s269 +
+            s270 + s271 + s272 + s273 + s274 + s275 + s276 + s277 + s278 + s279 +
+            s280 + s281 + s282 + s283 + s284 + s285 + s286 + s287 + s288 + s289 +
+            s290 + s291 + s292 + s293 + s294 + s295 + s296 + s297 + s298 + s299;
+
+       StringBuilder sb = new StringBuilder();
+       for (int c = 0; c < 300; c++) {
+            sb.append(String.format("s%03d", c));
+       }
+       test(sb.toString(), res);
+    }
+
+    public static void test(String expected, String actual) {
+       // Fingers crossed: String concat should work.
+       if (!expected.equals(actual)) {
+          throw new IllegalStateException("Expected = " + expected + ", actual = " + actual);
+       }
+    }
+}
+
diff --git a/jdk/test/java/lang/String/concat/ImplicitStringConcatManyLongs.java b/jdk/test/java/lang/String/concat/ImplicitStringConcatManyLongs.java
new file mode 100644
index 00000000000..cc14a100cec
--- /dev/null
+++ b/jdk/test/java/lang/String/concat/ImplicitStringConcatManyLongs.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ * @summary Test implicit String concatenations with lots of arguments (two-slot version)
+ *
+ * @compile ImplicitStringConcatManyLongs.java
+ * @run main/othervm ImplicitStringConcatManyLongs
+ *
+ * @compile -XDstringConcat=inline ImplicitStringConcatManyLongs.java
+ * @run main/othervm ImplicitStringConcatManyLongs
+ *
+ * @compile -XDstringConcat=indy -source 1.9 -target 1.9 ImplicitStringConcatManyLongs.java
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              ImplicitStringConcatManyLongs
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatManyLongs
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatManyLongs
+
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatManyLongs
+ *
+ * @compile -XDstringConcat=indyWithConstants -source 1.9 -target 1.9 ImplicitStringConcatManyLongs.java
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              ImplicitStringConcatManyLongs
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatManyLongs
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatManyLongs
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatManyLongs
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatManyLongs
+*/
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+public class ImplicitStringConcatManyLongs {
+
+    static long s000, s001, s002, s003, s004, s005, s006, s007, s008, s009;
+    static long s010, s011, s012, s013, s014, s015, s016, s017, s018, s019;
+    static long s020, s021, s022, s023, s024, s025, s026, s027, s028, s029;
+    static long s030, s031, s032, s033, s034, s035, s036, s037, s038, s039;
+    static long s040, s041, s042, s043, s044, s045, s046, s047, s048, s049;
+    static long s050, s051, s052, s053, s054, s055, s056, s057, s058, s059;
+    static long s060, s061, s062, s063, s064, s065, s066, s067, s068, s069;
+    static long s070, s071, s072, s073, s074, s075, s076, s077, s078, s079;
+    static long s080, s081, s082, s083, s084, s085, s086, s087, s088, s089;
+    static long s090, s091, s092, s093, s094, s095, s096, s097, s098, s099;
+
+    static long s100, s101, s102, s103, s104, s105, s106, s107, s108, s109;
+    static long s110, s111, s112, s113, s114, s115, s116, s117, s118, s119;
+    static long s120, s121, s122, s123, s124, s125, s126, s127, s128, s129;
+    static long s130, s131, s132, s133, s134, s135, s136, s137, s138, s139;
+    static long s140, s141, s142, s143, s144, s145, s146, s147, s148, s149;
+    static long s150, s151, s152, s153, s154, s155, s156, s157, s158, s159;
+    static long s160, s161, s162, s163, s164, s165, s166, s167, s168, s169;
+    static long s170, s171, s172, s173, s174, s175, s176, s177, s178, s179;
+    static long s180, s181, s182, s183, s184, s185, s186, s187, s188, s189;
+    static long s190, s191, s192, s193, s194, s195, s196, s197, s198, s199;
+
+    static long s200, s201, s202, s203, s204, s205, s206, s207, s208, s209;
+    static long s210, s211, s212, s213, s214, s215, s216, s217, s218, s219;
+    static long s220, s221, s222, s223, s224, s225, s226, s227, s228, s229;
+    static long s230, s231, s232, s233, s234, s235, s236, s237, s238, s239;
+    static long s240, s241, s242, s243, s244, s245, s246, s247, s248, s249;
+    static long s250, s251, s252, s253, s254, s255, s256, s257, s258, s259;
+    static long s260, s261, s262, s263, s264, s265, s266, s267, s268, s269;
+    static long s270, s271, s272, s273, s274, s275, s276, s277, s278, s279;
+    static long s280, s281, s282, s283, s284, s285, s286, s287, s288, s289;
+    static long s290, s291, s292, s293, s294, s295, s296, s297, s298, s299;
+
+    static {
+        for (Field f : ImplicitStringConcatManyLongs.class.getDeclaredFields()) {
+            if (Modifier.isStatic(f.getModifiers())) {
+                String name = f.getName();
+                try {
+                    f.set(null, Long.valueOf(name.substring(1)));
+                } catch (IllegalAccessException e) {
+                    throw new IllegalStateException(e);
+                }
+            }
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        String res = "" +
+            s000 + s001 + s002 + s003 + s004 + s005 + s006 + s007 + s008 + s009 +
+            s010 + s011 + s012 + s013 + s014 + s015 + s016 + s017 + s018 + s019 +
+            s020 + s021 + s022 + s023 + s024 + s025 + s026 + s027 + s028 + s029 +
+            s030 + s031 + s032 + s033 + s034 + s035 + s036 + s037 + s038 + s039 +
+            s040 + s041 + s042 + s043 + s044 + s045 + s046 + s047 + s048 + s049 +
+            s050 + s051 + s052 + s053 + s054 + s055 + s056 + s057 + s058 + s059 +
+            s060 + s061 + s062 + s063 + s064 + s065 + s066 + s067 + s068 + s069 +
+            s070 + s071 + s072 + s073 + s074 + s075 + s076 + s077 + s078 + s079 +
+            s080 + s081 + s082 + s083 + s084 + s085 + s086 + s087 + s088 + s089 +
+            s090 + s091 + s092 + s093 + s094 + s095 + s096 + s097 + s098 + s099 +
+
+            s100 + s101 + s102 + s103 + s104 + s105 + s106 + s107 + s108 + s109 +
+            s110 + s111 + s112 + s113 + s114 + s115 + s116 + s117 + s118 + s119 +
+            s120 + s121 + s122 + s123 + s124 + s125 + s126 + s127 + s128 + s129 +
+            s130 + s131 + s132 + s133 + s134 + s135 + s136 + s137 + s138 + s139 +
+            s140 + s141 + s142 + s143 + s144 + s145 + s146 + s147 + s148 + s149 +
+            s150 + s151 + s152 + s153 + s154 + s155 + s156 + s157 + s158 + s159 +
+            s160 + s161 + s162 + s163 + s164 + s165 + s166 + s167 + s168 + s169 +
+            s170 + s171 + s172 + s173 + s174 + s175 + s176 + s177 + s178 + s179 +
+            s180 + s181 + s182 + s183 + s184 + s185 + s186 + s187 + s188 + s189 +
+            s190 + s191 + s192 + s193 + s194 + s195 + s196 + s197 + s198 + s199 +
+
+            s200 + s201 + s202 + s203 + s204 + s205 + s206 + s207 + s208 + s209 +
+            s210 + s211 + s212 + s213 + s214 + s215 + s216 + s217 + s218 + s219 +
+            s220 + s221 + s222 + s223 + s224 + s225 + s226 + s227 + s228 + s229 +
+            s230 + s231 + s232 + s233 + s234 + s235 + s236 + s237 + s238 + s239 +
+            s240 + s241 + s242 + s243 + s244 + s245 + s246 + s247 + s248 + s249 +
+            s250 + s251 + s252 + s253 + s254 + s255 + s256 + s257 + s258 + s259 +
+            s260 + s261 + s262 + s263 + s264 + s265 + s266 + s267 + s268 + s269 +
+            s270 + s271 + s272 + s273 + s274 + s275 + s276 + s277 + s278 + s279 +
+            s280 + s281 + s282 + s283 + s284 + s285 + s286 + s287 + s288 + s289 +
+            s290 + s291 + s292 + s293 + s294 + s295 + s296 + s297 + s298 + s299;
+
+       StringBuilder sb = new StringBuilder();
+       for (int c = 0; c < 300; c++) {
+            sb.append(c);
+       }
+       test(sb.toString(), res);
+    }
+
+    public static void test(String expected, String actual) {
+       // Fingers crossed: String concat should work.
+       if (!expected.equals(actual)) {
+          throw new IllegalStateException("Expected = " + expected + ", actual = " + actual);
+       }
+    }
+}
+
diff --git a/jdk/test/java/lang/String/concat/ImplicitStringConcatShapes-head.template b/jdk/test/java/lang/String/concat/ImplicitStringConcatShapes-head.template
new file mode 100644
index 00000000000..10fd57f0616
--- /dev/null
+++ b/jdk/test/java/lang/String/concat/ImplicitStringConcatShapes-head.template
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ * @summary Test implicit String concatenations, multiple shapes.
+ *
+ * @compile ImplicitStringConcatShapes.java
+ * @run main/othervm ImplicitStringConcatShapes
+ *
+ * @compile -XDstringConcat=inline ImplicitStringConcatShapes.java
+ * @run main/othervm ImplicitStringConcatShapes
+ *
+ * @compile -XDstringConcat=indy -source 1.9 -target 1.9 ImplicitStringConcatShapes.java
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              ImplicitStringConcatShapes
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatShapes
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ *
+ * @compile -XDstringConcat=indyWithConstants -source 1.9 -target 1.9 ImplicitStringConcatShapes.java
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              ImplicitStringConcatShapes
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatShapes
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+*/
+public class ImplicitStringConcatShapes {
+    public static void test(String expected, String actual) {
+        // Fingers crossed: String concat should work.
+        if (!expected.equals(actual)) {
+            StringBuilder sb = new StringBuilder();
+            sb.append("Expected = ");
+            sb.append(expected);
+            sb.append(", actual = ");
+            sb.append(actual);
+            throw new IllegalStateException(sb.toString());
+        }
+    }
+
+    static class MyClass {
+        private final int i;
+
+        public MyClass(int i) {
+            this.i = i;
+        }
+
+        public String toString() {
+            return new StringBuilder("C(").append(i).append(")").toString();
+        }
+    }
+
+    static class MyClassNullToString {
+        public String toString() {
+            return null;
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        new ImplicitStringConcatShapes().run();
+    }
diff --git a/jdk/test/java/lang/String/concat/ImplicitStringConcatShapes.java b/jdk/test/java/lang/String/concat/ImplicitStringConcatShapes.java
new file mode 100644
index 00000000000..c4a673721ae
--- /dev/null
+++ b/jdk/test/java/lang/String/concat/ImplicitStringConcatShapes.java
@@ -0,0 +1,5931 @@
+/*
+ * Copyright (c) 2015, 2016, 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
+ * @summary Test implicit String concatenations, multiple shapes.
+ *
+ * @compile ImplicitStringConcatShapes.java
+ * @run main/othervm ImplicitStringConcatShapes
+ *
+ * @compile -XDstringConcat=inline ImplicitStringConcatShapes.java
+ * @run main/othervm ImplicitStringConcatShapes
+ *
+ * @compile -XDstringConcat=indy -source 1.9 -target 1.9 ImplicitStringConcatShapes.java
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              ImplicitStringConcatShapes
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatShapes
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ *
+ * @compile -XDstringConcat=indyWithConstants -source 1.9 -target 1.9 ImplicitStringConcatShapes.java
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              ImplicitStringConcatShapes
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  ImplicitStringConcatShapes
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  ImplicitStringConcatShapes
+*/
+public class ImplicitStringConcatShapes {
+    public static void test(String expected, String actual) {
+        // Fingers crossed: String concat should work.
+        if (!expected.equals(actual)) {
+            StringBuilder sb = new StringBuilder();
+            sb.append("Expected = ");
+            sb.append(expected);
+            sb.append(", actual = ");
+            sb.append(actual);
+            throw new IllegalStateException(sb.toString());
+        }
+    }
+
+    static class MyClass {
+        private final int i;
+
+        public MyClass(int i) {
+            this.i = i;
+        }
+
+        public String toString() {
+            return new StringBuilder("C(").append(i).append(")").toString();
+        }
+    }
+
+    static class MyClassNullToString {
+        public String toString() {
+            return null;
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        new ImplicitStringConcatShapes().run();
+    }
+    static final boolean sf_bl = true;
+    static final byte sf_b = 80;
+    static final byte sf_bM = -41;
+    static final char sf_c = 'C';
+    static final short sf_s = 5500;
+    static final short sf_sM = -8400;
+    static final int sf_i = 75000000;
+    static final int sf_iM = -2000000;
+    static final Integer sf_I = 1000000;
+    static final Integer sf_IN = null;
+    static final float sf_f = 17.0f;
+    static final float sf_fM = -42.0f;
+    static final long sf_l = -194313216L;
+    static final long sf_lM = -1705032704L;
+    static final double sf_d = 12.0d;
+    static final double sf_dM = -84.0d;
+    static final Object sf_o = new MyClass(87);
+    static final Object sf_oN = null;
+    static final Object sf_oNtS = new MyClassNullToString();
+    static final String sf_str = "75";
+    static final String sf_strU = "\u04511";
+    static final String sf_strU1 = "\u000151";
+    static final String sf_strU2 = "\u000292";
+    static final int[] sf_iAN = null;
+    static final Object[] sf_oAN = null;
+    static  boolean s_bl = true;
+    static  byte s_b = 25;
+    static  byte s_bM = -43;
+    static  char s_c = 'T';
+    static  short s_s = 3900;
+    static  short s_sM = -2900;
+    static  int s_i = 97000000;
+    static  int s_iM = -1000000;
+    static  Integer s_I = 25000000;
+    static  Integer s_IN = null;
+    static  float s_f = 55.0f;
+    static  float s_fM = -52.0f;
+    static  long s_l = 935228928L;
+    static  long s_lM = -1410065408L;
+    static  double s_d = 8.0d;
+    static  double s_dM = -96.0d;
+    static  Object s_o = new MyClass(82);
+    static  Object s_oN = null;
+    static  Object s_oNtS = new MyClassNullToString();
+    static  String s_str = "18";
+    static  String s_strU = "\u045180";
+    static  String s_strU1 = "\u000112";
+    static  String s_strU2 = "\u000291";
+    static  int[] s_iAN = null;
+    static  Object[] s_oAN = null;
+     final boolean f_bl = false;
+     final byte f_b = 44;
+     final byte f_bM = -54;
+     final char f_c = 'I';
+     final short f_s = 8000;
+     final short f_sM = -9900;
+     final int f_i = 58000000;
+     final int f_iM = -55000000;
+     final Integer f_I = 94000000;
+     final Integer f_IN = null;
+     final float f_f = 94.0f;
+     final float f_fM = -87.0f;
+     final long f_l = 1460392448L;
+     final long f_lM = -820130816L;
+     final double f_d = 83.0d;
+     final double f_dM = -99.0d;
+     final Object f_o = new MyClass(70);
+     final Object f_oN = null;
+     final Object f_oNtS = new MyClassNullToString();
+     final String f_str = "19";
+     final String f_strU = "\u045176";
+     final String f_strU1 = "\u000121";
+     final String f_strU2 = "\u000218";
+     final int[] f_iAN = null;
+     final Object[] f_oAN = null;
+
+    public void run() {
+        run0();
+        run1();
+        run2();
+        run3();
+        run4();
+        run5();
+    }
+
+    public void run0() {
+        test("-96.0", "" + s_dM);
+        test("null", "" + s_oNtS);
+        test("\u045176", "" + f_strU);
+        test("92", "" + sf_strU2);
+        test("51", "" + sf_strU1);
+        test("null", "" + s_iAN);
+        test("-54", "" + f_bM);
+        test("-87.0", "" + f_fM);
+        test("null", "" + s_oAN);
+        test("19", "" + f_str);
+        test("-41", "" + sf_bM);
+        test("null", "" + sf_IN);
+        test("T", "" + s_c);
+        test("-42.0", "" + sf_fM);
+        test("25", "" + s_b);
+        test("null", "" + f_oN);
+        test("-1410065408", "" + s_lM);
+        test("8.0", "" + s_d);
+        test("55.0", "" + s_f);
+        test("97000000", "" + s_i);
+        test("-9900", "" + f_sM);
+        test("935228928", "" + s_l);
+        test("-8400", "" + sf_sM);
+        test("C(82)", "" + s_o);
+        test("null", "" + sf_oNtS);
+        test("true", "" + s_bl);
+        test("3900", "" + s_s);
+        test("null", "" + sf_oN);
+        test("94000000", "" + f_I);
+        test("null", "" + f_IN);
+        test("true", "" + sf_bl);
+        test("5500", "" + sf_s);
+        test("-2900", "" + s_sM);
+        test("-194313216", "" + sf_l);
+        test("12", "" + s_strU1);
+        test("C(87)", "" + sf_o);
+        test("91", "" + s_strU2);
+        test("21", "" + f_strU1);
+        test("18", "" + f_strU2);
+        test("null", "" + f_iAN);
+        test("null", "" + s_oN);
+        test("\u045180", "" + s_strU);
+        test("C", "" + sf_c);
+        test("75", "" + sf_str);
+        test("-43", "" + s_bM);
+        test("80", "" + sf_b);
+        test("null", "" + s_IN);
+        test("-52.0", "" + s_fM);
+        test("75000000", "" + sf_i);
+        test("44", "" + f_b);
+        test("-1705032704", "" + sf_lM);
+        test("null", "" + f_oAN);
+        test("83.0", "" + f_d);
+        test("I", "" + f_c);
+        test("94.0", "" + f_f);
+        test("12.0", "" + sf_d);
+        test("-99.0", "" + f_dM);
+        test("17.0", "" + sf_f);
+        test("-84.0", "" + sf_dM);
+        test("58000000", "" + f_i);
+        test("-55000000", "" + f_iM);
+        test("1460392448", "" + f_l);
+        test("C(70)", "" + f_o);
+        test("\u04511", "" + sf_strU);
+        test("8000", "" + f_s);
+        test("18", "" + s_str);
+        test("-1000000", "" + s_iM);
+        test("1000000", "" + sf_I);
+        test("null", "" + f_oNtS);
+        test("false", "" + f_bl);
+        test("null", "" + sf_iAN);
+        test("-2000000", "" + sf_iM);
+        test("-820130816", "" + f_lM);
+        test("null", "" + sf_oAN);
+        test("25000000", "" + s_I);
+        test("-96.0-96.0", "" + s_dM + s_dM);
+        test("-96.0null", "" + s_dM + s_oNtS);
+        test("-96.0\u045176", "" + s_dM + f_strU);
+        test("-96.092", "" + s_dM + sf_strU2);
+        test("-96.051", "" + s_dM + sf_strU1);
+        test("-96.0null", "" + s_dM + s_iAN);
+        test("-96.0-54", "" + s_dM + f_bM);
+        test("-96.0-87.0", "" + s_dM + f_fM);
+        test("-96.0null", "" + s_dM + s_oAN);
+        test("-96.019", "" + s_dM + f_str);
+        test("-96.0-41", "" + s_dM + sf_bM);
+        test("-96.0null", "" + s_dM + sf_IN);
+        test("-96.0T", "" + s_dM + s_c);
+        test("-96.0-42.0", "" + s_dM + sf_fM);
+        test("-96.025", "" + s_dM + s_b);
+        test("-96.0null", "" + s_dM + f_oN);
+        test("-96.0-1410065408", "" + s_dM + s_lM);
+        test("-96.08.0", "" + s_dM + s_d);
+        test("-96.055.0", "" + s_dM + s_f);
+        test("-96.097000000", "" + s_dM + s_i);
+        test("-96.0-9900", "" + s_dM + f_sM);
+        test("-96.0935228928", "" + s_dM + s_l);
+        test("-96.0-8400", "" + s_dM + sf_sM);
+        test("-96.0C(82)", "" + s_dM + s_o);
+        test("-96.0null", "" + s_dM + sf_oNtS);
+        test("-96.0true", "" + s_dM + s_bl);
+        test("-96.03900", "" + s_dM + s_s);
+        test("-96.0null", "" + s_dM + sf_oN);
+        test("-96.094000000", "" + s_dM + f_I);
+        test("-96.0null", "" + s_dM + f_IN);
+        test("-96.0true", "" + s_dM + sf_bl);
+        test("-96.05500", "" + s_dM + sf_s);
+        test("-96.0-2900", "" + s_dM + s_sM);
+        test("-96.0-194313216", "" + s_dM + sf_l);
+        test("-96.012", "" + s_dM + s_strU1);
+        test("-96.0C(87)", "" + s_dM + sf_o);
+        test("-96.091", "" + s_dM + s_strU2);
+        test("-96.021", "" + s_dM + f_strU1);
+        test("-96.018", "" + s_dM + f_strU2);
+        test("-96.0null", "" + s_dM + f_iAN);
+        test("-96.0null", "" + s_dM + s_oN);
+        test("-96.0\u045180", "" + s_dM + s_strU);
+        test("-96.0C", "" + s_dM + sf_c);
+        test("-96.075", "" + s_dM + sf_str);
+        test("-96.0-43", "" + s_dM + s_bM);
+        test("-96.080", "" + s_dM + sf_b);
+        test("-96.0null", "" + s_dM + s_IN);
+        test("-96.0-52.0", "" + s_dM + s_fM);
+        test("-96.075000000", "" + s_dM + sf_i);
+        test("-96.044", "" + s_dM + f_b);
+        test("-96.0-1705032704", "" + s_dM + sf_lM);
+        test("-96.0null", "" + s_dM + f_oAN);
+        test("-96.083.0", "" + s_dM + f_d);
+        test("-96.0I", "" + s_dM + f_c);
+        test("-96.094.0", "" + s_dM + f_f);
+        test("-96.012.0", "" + s_dM + sf_d);
+        test("-96.0-99.0", "" + s_dM + f_dM);
+        test("-96.017.0", "" + s_dM + sf_f);
+        test("-96.0-84.0", "" + s_dM + sf_dM);
+        test("-96.058000000", "" + s_dM + f_i);
+        test("-96.0-55000000", "" + s_dM + f_iM);
+        test("-96.01460392448", "" + s_dM + f_l);
+        test("-96.0C(70)", "" + s_dM + f_o);
+        test("-96.0\u04511", "" + s_dM + sf_strU);
+        test("-96.08000", "" + s_dM + f_s);
+        test("-96.018", "" + s_dM + s_str);
+        test("-96.0-1000000", "" + s_dM + s_iM);
+        test("-96.01000000", "" + s_dM + sf_I);
+        test("-96.0null", "" + s_dM + f_oNtS);
+        test("-96.0false", "" + s_dM + f_bl);
+        test("-96.0null", "" + s_dM + sf_iAN);
+        test("-96.0-2000000", "" + s_dM + sf_iM);
+        test("-96.0-820130816", "" + s_dM + f_lM);
+        test("-96.0null", "" + s_dM + sf_oAN);
+        test("-96.025000000", "" + s_dM + s_I);
+        test("null-96.0", "" + s_oNtS + s_dM);
+        test("nullnull", "" + s_oNtS + s_oNtS);
+        test("null\u045176", "" + s_oNtS + f_strU);
+        test("null92", "" + s_oNtS + sf_strU2);
+        test("null51", "" + s_oNtS + sf_strU1);
+        test("nullnull", "" + s_oNtS + s_iAN);
+        test("null-54", "" + s_oNtS + f_bM);
+        test("null-87.0", "" + s_oNtS + f_fM);
+        test("nullnull", "" + s_oNtS + s_oAN);
+        test("null19", "" + s_oNtS + f_str);
+        test("null-41", "" + s_oNtS + sf_bM);
+        test("nullnull", "" + s_oNtS + sf_IN);
+        test("nullT", "" + s_oNtS + s_c);
+        test("null-42.0", "" + s_oNtS + sf_fM);
+        test("null25", "" + s_oNtS + s_b);
+        test("nullnull", "" + s_oNtS + f_oN);
+        test("null-1410065408", "" + s_oNtS + s_lM);
+        test("null8.0", "" + s_oNtS + s_d);
+        test("null55.0", "" + s_oNtS + s_f);
+        test("null97000000", "" + s_oNtS + s_i);
+        test("null-9900", "" + s_oNtS + f_sM);
+        test("null935228928", "" + s_oNtS + s_l);
+        test("null-8400", "" + s_oNtS + sf_sM);
+        test("nullC(82)", "" + s_oNtS + s_o);
+        test("nullnull", "" + s_oNtS + sf_oNtS);
+        test("nulltrue", "" + s_oNtS + s_bl);
+        test("null3900", "" + s_oNtS + s_s);
+        test("nullnull", "" + s_oNtS + sf_oN);
+        test("null94000000", "" + s_oNtS + f_I);
+        test("nullnull", "" + s_oNtS + f_IN);
+        test("nulltrue", "" + s_oNtS + sf_bl);
+        test("null5500", "" + s_oNtS + sf_s);
+        test("null-2900", "" + s_oNtS + s_sM);
+        test("null-194313216", "" + s_oNtS + sf_l);
+        test("null12", "" + s_oNtS + s_strU1);
+        test("nullC(87)", "" + s_oNtS + sf_o);
+        test("null91", "" + s_oNtS + s_strU2);
+        test("null21", "" + s_oNtS + f_strU1);
+        test("null18", "" + s_oNtS + f_strU2);
+        test("nullnull", "" + s_oNtS + f_iAN);
+        test("nullnull", "" + s_oNtS + s_oN);
+        test("null\u045180", "" + s_oNtS + s_strU);
+        test("nullC", "" + s_oNtS + sf_c);
+        test("null75", "" + s_oNtS + sf_str);
+        test("null-43", "" + s_oNtS + s_bM);
+        test("null80", "" + s_oNtS + sf_b);
+        test("nullnull", "" + s_oNtS + s_IN);
+        test("null-52.0", "" + s_oNtS + s_fM);
+        test("null75000000", "" + s_oNtS + sf_i);
+        test("null44", "" + s_oNtS + f_b);
+        test("null-1705032704", "" + s_oNtS + sf_lM);
+        test("nullnull", "" + s_oNtS + f_oAN);
+        test("null83.0", "" + s_oNtS + f_d);
+        test("nullI", "" + s_oNtS + f_c);
+        test("null94.0", "" + s_oNtS + f_f);
+        test("null12.0", "" + s_oNtS + sf_d);
+        test("null-99.0", "" + s_oNtS + f_dM);
+        test("null17.0", "" + s_oNtS + sf_f);
+        test("null-84.0", "" + s_oNtS + sf_dM);
+        test("null58000000", "" + s_oNtS + f_i);
+        test("null-55000000", "" + s_oNtS + f_iM);
+        test("null1460392448", "" + s_oNtS + f_l);
+        test("nullC(70)", "" + s_oNtS + f_o);
+        test("null\u04511", "" + s_oNtS + sf_strU);
+        test("null8000", "" + s_oNtS + f_s);
+        test("null18", "" + s_oNtS + s_str);
+        test("null-1000000", "" + s_oNtS + s_iM);
+        test("null1000000", "" + s_oNtS + sf_I);
+        test("nullnull", "" + s_oNtS + f_oNtS);
+        test("nullfalse", "" + s_oNtS + f_bl);
+        test("nullnull", "" + s_oNtS + sf_iAN);
+        test("null-2000000", "" + s_oNtS + sf_iM);
+        test("null-820130816", "" + s_oNtS + f_lM);
+        test("nullnull", "" + s_oNtS + sf_oAN);
+        test("null25000000", "" + s_oNtS + s_I);
+        test("\u045176-96.0", "" + f_strU + s_dM);
+        test("\u045176null", "" + f_strU + s_oNtS);
+        test("\u045176\u045176", "" + f_strU + f_strU);
+        test("\u04517692", "" + f_strU + sf_strU2);
+        test("\u04517651", "" + f_strU + sf_strU1);
+        test("\u045176null", "" + f_strU + s_iAN);
+        test("\u045176-54", "" + f_strU + f_bM);
+        test("\u045176-87.0", "" + f_strU + f_fM);
+        test("\u045176null", "" + f_strU + s_oAN);
+        test("\u04517619", "" + f_strU + f_str);
+        test("\u045176-41", "" + f_strU + sf_bM);
+        test("\u045176null", "" + f_strU + sf_IN);
+        test("\u045176T", "" + f_strU + s_c);
+        test("\u045176-42.0", "" + f_strU + sf_fM);
+        test("\u04517625", "" + f_strU + s_b);
+        test("\u045176null", "" + f_strU + f_oN);
+        test("\u045176-1410065408", "" + f_strU + s_lM);
+        test("\u0451768.0", "" + f_strU + s_d);
+        test("\u04517655.0", "" + f_strU + s_f);
+        test("\u04517697000000", "" + f_strU + s_i);
+        test("\u045176-9900", "" + f_strU + f_sM);
+        test("\u045176935228928", "" + f_strU + s_l);
+        test("\u045176-8400", "" + f_strU + sf_sM);
+        test("\u045176C(82)", "" + f_strU + s_o);
+        test("\u045176null", "" + f_strU + sf_oNtS);
+        test("\u045176true", "" + f_strU + s_bl);
+        test("\u0451763900", "" + f_strU + s_s);
+        test("\u045176null", "" + f_strU + sf_oN);
+        test("\u04517694000000", "" + f_strU + f_I);
+        test("\u045176null", "" + f_strU + f_IN);
+        test("\u045176true", "" + f_strU + sf_bl);
+        test("\u0451765500", "" + f_strU + sf_s);
+        test("\u045176-2900", "" + f_strU + s_sM);
+        test("\u045176-194313216", "" + f_strU + sf_l);
+        test("\u04517612", "" + f_strU + s_strU1);
+        test("\u045176C(87)", "" + f_strU + sf_o);
+        test("\u04517691", "" + f_strU + s_strU2);
+        test("\u04517621", "" + f_strU + f_strU1);
+        test("\u04517618", "" + f_strU + f_strU2);
+        test("\u045176null", "" + f_strU + f_iAN);
+        test("\u045176null", "" + f_strU + s_oN);
+        test("\u045176\u045180", "" + f_strU + s_strU);
+        test("\u045176C", "" + f_strU + sf_c);
+        test("\u04517675", "" + f_strU + sf_str);
+        test("\u045176-43", "" + f_strU + s_bM);
+        test("\u04517680", "" + f_strU + sf_b);
+        test("\u045176null", "" + f_strU + s_IN);
+        test("\u045176-52.0", "" + f_strU + s_fM);
+        test("\u04517675000000", "" + f_strU + sf_i);
+        test("\u04517644", "" + f_strU + f_b);
+        test("\u045176-1705032704", "" + f_strU + sf_lM);
+        test("\u045176null", "" + f_strU + f_oAN);
+        test("\u04517683.0", "" + f_strU + f_d);
+        test("\u045176I", "" + f_strU + f_c);
+        test("\u04517694.0", "" + f_strU + f_f);
+        test("\u04517612.0", "" + f_strU + sf_d);
+        test("\u045176-99.0", "" + f_strU + f_dM);
+        test("\u04517617.0", "" + f_strU + sf_f);
+        test("\u045176-84.0", "" + f_strU + sf_dM);
+        test("\u04517658000000", "" + f_strU + f_i);
+        test("\u045176-55000000", "" + f_strU + f_iM);
+        test("\u0451761460392448", "" + f_strU + f_l);
+        test("\u045176C(70)", "" + f_strU + f_o);
+        test("\u045176\u04511", "" + f_strU + sf_strU);
+        test("\u0451768000", "" + f_strU + f_s);
+        test("\u04517618", "" + f_strU + s_str);
+        test("\u045176-1000000", "" + f_strU + s_iM);
+        test("\u0451761000000", "" + f_strU + sf_I);
+        test("\u045176null", "" + f_strU + f_oNtS);
+        test("\u045176false", "" + f_strU + f_bl);
+        test("\u045176null", "" + f_strU + sf_iAN);
+        test("\u045176-2000000", "" + f_strU + sf_iM);
+        test("\u045176-820130816", "" + f_strU + f_lM);
+        test("\u045176null", "" + f_strU + sf_oAN);
+        test("\u04517625000000", "" + f_strU + s_I);
+        test("92-96.0", "" + sf_strU2 + s_dM);
+        test("92null", "" + sf_strU2 + s_oNtS);
+        test("92\u045176", "" + sf_strU2 + f_strU);
+        test("9292", "" + sf_strU2 + sf_strU2);
+        test("9251", "" + sf_strU2 + sf_strU1);
+        test("92null", "" + sf_strU2 + s_iAN);
+        test("92-54", "" + sf_strU2 + f_bM);
+        test("92-87.0", "" + sf_strU2 + f_fM);
+        test("92null", "" + sf_strU2 + s_oAN);
+        test("9219", "" + sf_strU2 + f_str);
+        test("92-41", "" + sf_strU2 + sf_bM);
+        test("92null", "" + sf_strU2 + sf_IN);
+        test("92T", "" + sf_strU2 + s_c);
+        test("92-42.0", "" + sf_strU2 + sf_fM);
+        test("9225", "" + sf_strU2 + s_b);
+        test("92null", "" + sf_strU2 + f_oN);
+        test("92-1410065408", "" + sf_strU2 + s_lM);
+        test("928.0", "" + sf_strU2 + s_d);
+        test("9255.0", "" + sf_strU2 + s_f);
+        test("9297000000", "" + sf_strU2 + s_i);
+        test("92-9900", "" + sf_strU2 + f_sM);
+        test("92935228928", "" + sf_strU2 + s_l);
+        test("92-8400", "" + sf_strU2 + sf_sM);
+        test("92C(82)", "" + sf_strU2 + s_o);
+        test("92null", "" + sf_strU2 + sf_oNtS);
+        test("92true", "" + sf_strU2 + s_bl);
+        test("923900", "" + sf_strU2 + s_s);
+        test("92null", "" + sf_strU2 + sf_oN);
+        test("9294000000", "" + sf_strU2 + f_I);
+        test("92null", "" + sf_strU2 + f_IN);
+        test("92true", "" + sf_strU2 + sf_bl);
+        test("925500", "" + sf_strU2 + sf_s);
+        test("92-2900", "" + sf_strU2 + s_sM);
+        test("92-194313216", "" + sf_strU2 + sf_l);
+        test("9212", "" + sf_strU2 + s_strU1);
+        test("92C(87)", "" + sf_strU2 + sf_o);
+        test("9291", "" + sf_strU2 + s_strU2);
+        test("9221", "" + sf_strU2 + f_strU1);
+        test("9218", "" + sf_strU2 + f_strU2);
+        test("92null", "" + sf_strU2 + f_iAN);
+        test("92null", "" + sf_strU2 + s_oN);
+        test("92\u045180", "" + sf_strU2 + s_strU);
+        test("92C", "" + sf_strU2 + sf_c);
+        test("9275", "" + sf_strU2 + sf_str);
+        test("92-43", "" + sf_strU2 + s_bM);
+        test("9280", "" + sf_strU2 + sf_b);
+        test("92null", "" + sf_strU2 + s_IN);
+        test("92-52.0", "" + sf_strU2 + s_fM);
+        test("9275000000", "" + sf_strU2 + sf_i);
+        test("9244", "" + sf_strU2 + f_b);
+        test("92-1705032704", "" + sf_strU2 + sf_lM);
+        test("92null", "" + sf_strU2 + f_oAN);
+        test("9283.0", "" + sf_strU2 + f_d);
+        test("92I", "" + sf_strU2 + f_c);
+        test("9294.0", "" + sf_strU2 + f_f);
+        test("9212.0", "" + sf_strU2 + sf_d);
+        test("92-99.0", "" + sf_strU2 + f_dM);
+        test("9217.0", "" + sf_strU2 + sf_f);
+        test("92-84.0", "" + sf_strU2 + sf_dM);
+        test("9258000000", "" + sf_strU2 + f_i);
+        test("92-55000000", "" + sf_strU2 + f_iM);
+        test("921460392448", "" + sf_strU2 + f_l);
+        test("92C(70)", "" + sf_strU2 + f_o);
+        test("92\u04511", "" + sf_strU2 + sf_strU);
+        test("928000", "" + sf_strU2 + f_s);
+        test("9218", "" + sf_strU2 + s_str);
+        test("92-1000000", "" + sf_strU2 + s_iM);
+        test("921000000", "" + sf_strU2 + sf_I);
+        test("92null", "" + sf_strU2 + f_oNtS);
+        test("92false", "" + sf_strU2 + f_bl);
+        test("92null", "" + sf_strU2 + sf_iAN);
+        test("92-2000000", "" + sf_strU2 + sf_iM);
+        test("92-820130816", "" + sf_strU2 + f_lM);
+        test("92null", "" + sf_strU2 + sf_oAN);
+        test("9225000000", "" + sf_strU2 + s_I);
+        test("51-96.0", "" + sf_strU1 + s_dM);
+        test("51null", "" + sf_strU1 + s_oNtS);
+        test("51\u045176", "" + sf_strU1 + f_strU);
+        test("5192", "" + sf_strU1 + sf_strU2);
+        test("5151", "" + sf_strU1 + sf_strU1);
+        test("51null", "" + sf_strU1 + s_iAN);
+        test("51-54", "" + sf_strU1 + f_bM);
+        test("51-87.0", "" + sf_strU1 + f_fM);
+        test("51null", "" + sf_strU1 + s_oAN);
+        test("5119", "" + sf_strU1 + f_str);
+        test("51-41", "" + sf_strU1 + sf_bM);
+        test("51null", "" + sf_strU1 + sf_IN);
+        test("51T", "" + sf_strU1 + s_c);
+        test("51-42.0", "" + sf_strU1 + sf_fM);
+        test("5125", "" + sf_strU1 + s_b);
+        test("51null", "" + sf_strU1 + f_oN);
+        test("51-1410065408", "" + sf_strU1 + s_lM);
+        test("518.0", "" + sf_strU1 + s_d);
+        test("5155.0", "" + sf_strU1 + s_f);
+        test("5197000000", "" + sf_strU1 + s_i);
+        test("51-9900", "" + sf_strU1 + f_sM);
+        test("51935228928", "" + sf_strU1 + s_l);
+        test("51-8400", "" + sf_strU1 + sf_sM);
+        test("51C(82)", "" + sf_strU1 + s_o);
+        test("51null", "" + sf_strU1 + sf_oNtS);
+        test("51true", "" + sf_strU1 + s_bl);
+        test("513900", "" + sf_strU1 + s_s);
+        test("51null", "" + sf_strU1 + sf_oN);
+        test("5194000000", "" + sf_strU1 + f_I);
+        test("51null", "" + sf_strU1 + f_IN);
+        test("51true", "" + sf_strU1 + sf_bl);
+        test("515500", "" + sf_strU1 + sf_s);
+        test("51-2900", "" + sf_strU1 + s_sM);
+        test("51-194313216", "" + sf_strU1 + sf_l);
+        test("5112", "" + sf_strU1 + s_strU1);
+        test("51C(87)", "" + sf_strU1 + sf_o);
+        test("5191", "" + sf_strU1 + s_strU2);
+        test("5121", "" + sf_strU1 + f_strU1);
+        test("5118", "" + sf_strU1 + f_strU2);
+        test("51null", "" + sf_strU1 + f_iAN);
+        test("51null", "" + sf_strU1 + s_oN);
+        test("51\u045180", "" + sf_strU1 + s_strU);
+        test("51C", "" + sf_strU1 + sf_c);
+        test("5175", "" + sf_strU1 + sf_str);
+        test("51-43", "" + sf_strU1 + s_bM);
+        test("5180", "" + sf_strU1 + sf_b);
+        test("51null", "" + sf_strU1 + s_IN);
+        test("51-52.0", "" + sf_strU1 + s_fM);
+        test("5175000000", "" + sf_strU1 + sf_i);
+        test("5144", "" + sf_strU1 + f_b);
+        test("51-1705032704", "" + sf_strU1 + sf_lM);
+        test("51null", "" + sf_strU1 + f_oAN);
+        test("5183.0", "" + sf_strU1 + f_d);
+        test("51I", "" + sf_strU1 + f_c);
+        test("5194.0", "" + sf_strU1 + f_f);
+        test("5112.0", "" + sf_strU1 + sf_d);
+        test("51-99.0", "" + sf_strU1 + f_dM);
+        test("5117.0", "" + sf_strU1 + sf_f);
+        test("51-84.0", "" + sf_strU1 + sf_dM);
+        test("5158000000", "" + sf_strU1 + f_i);
+        test("51-55000000", "" + sf_strU1 + f_iM);
+        test("511460392448", "" + sf_strU1 + f_l);
+        test("51C(70)", "" + sf_strU1 + f_o);
+        test("51\u04511", "" + sf_strU1 + sf_strU);
+        test("518000", "" + sf_strU1 + f_s);
+        test("5118", "" + sf_strU1 + s_str);
+        test("51-1000000", "" + sf_strU1 + s_iM);
+        test("511000000", "" + sf_strU1 + sf_I);
+        test("51null", "" + sf_strU1 + f_oNtS);
+        test("51false", "" + sf_strU1 + f_bl);
+        test("51null", "" + sf_strU1 + sf_iAN);
+        test("51-2000000", "" + sf_strU1 + sf_iM);
+        test("51-820130816", "" + sf_strU1 + f_lM);
+        test("51null", "" + sf_strU1 + sf_oAN);
+        test("5125000000", "" + sf_strU1 + s_I);
+        test("null-96.0", "" + s_iAN + s_dM);
+        test("nullnull", "" + s_iAN + s_oNtS);
+        test("null\u045176", "" + s_iAN + f_strU);
+        test("null92", "" + s_iAN + sf_strU2);
+        test("null51", "" + s_iAN + sf_strU1);
+        test("nullnull", "" + s_iAN + s_iAN);
+        test("null-54", "" + s_iAN + f_bM);
+        test("null-87.0", "" + s_iAN + f_fM);
+        test("nullnull", "" + s_iAN + s_oAN);
+        test("null19", "" + s_iAN + f_str);
+        test("null-41", "" + s_iAN + sf_bM);
+        test("nullnull", "" + s_iAN + sf_IN);
+        test("nullT", "" + s_iAN + s_c);
+        test("null-42.0", "" + s_iAN + sf_fM);
+        test("null25", "" + s_iAN + s_b);
+        test("nullnull", "" + s_iAN + f_oN);
+        test("null-1410065408", "" + s_iAN + s_lM);
+        test("null8.0", "" + s_iAN + s_d);
+        test("null55.0", "" + s_iAN + s_f);
+        test("null97000000", "" + s_iAN + s_i);
+        test("null-9900", "" + s_iAN + f_sM);
+        test("null935228928", "" + s_iAN + s_l);
+        test("null-8400", "" + s_iAN + sf_sM);
+        test("nullC(82)", "" + s_iAN + s_o);
+        test("nullnull", "" + s_iAN + sf_oNtS);
+        test("nulltrue", "" + s_iAN + s_bl);
+        test("null3900", "" + s_iAN + s_s);
+        test("nullnull", "" + s_iAN + sf_oN);
+        test("null94000000", "" + s_iAN + f_I);
+        test("nullnull", "" + s_iAN + f_IN);
+        test("nulltrue", "" + s_iAN + sf_bl);
+        test("null5500", "" + s_iAN + sf_s);
+        test("null-2900", "" + s_iAN + s_sM);
+        test("null-194313216", "" + s_iAN + sf_l);
+        test("null12", "" + s_iAN + s_strU1);
+        test("nullC(87)", "" + s_iAN + sf_o);
+        test("null91", "" + s_iAN + s_strU2);
+        test("null21", "" + s_iAN + f_strU1);
+        test("null18", "" + s_iAN + f_strU2);
+        test("nullnull", "" + s_iAN + f_iAN);
+        test("nullnull", "" + s_iAN + s_oN);
+        test("null\u045180", "" + s_iAN + s_strU);
+        test("nullC", "" + s_iAN + sf_c);
+        test("null75", "" + s_iAN + sf_str);
+        test("null-43", "" + s_iAN + s_bM);
+        test("null80", "" + s_iAN + sf_b);
+        test("nullnull", "" + s_iAN + s_IN);
+        test("null-52.0", "" + s_iAN + s_fM);
+        test("null75000000", "" + s_iAN + sf_i);
+        test("null44", "" + s_iAN + f_b);
+        test("null-1705032704", "" + s_iAN + sf_lM);
+        test("nullnull", "" + s_iAN + f_oAN);
+        test("null83.0", "" + s_iAN + f_d);
+        test("nullI", "" + s_iAN + f_c);
+        test("null94.0", "" + s_iAN + f_f);
+        test("null12.0", "" + s_iAN + sf_d);
+        test("null-99.0", "" + s_iAN + f_dM);
+        test("null17.0", "" + s_iAN + sf_f);
+        test("null-84.0", "" + s_iAN + sf_dM);
+        test("null58000000", "" + s_iAN + f_i);
+        test("null-55000000", "" + s_iAN + f_iM);
+        test("null1460392448", "" + s_iAN + f_l);
+        test("nullC(70)", "" + s_iAN + f_o);
+        test("null\u04511", "" + s_iAN + sf_strU);
+        test("null8000", "" + s_iAN + f_s);
+        test("null18", "" + s_iAN + s_str);
+        test("null-1000000", "" + s_iAN + s_iM);
+        test("null1000000", "" + s_iAN + sf_I);
+        test("nullnull", "" + s_iAN + f_oNtS);
+        test("nullfalse", "" + s_iAN + f_bl);
+        test("nullnull", "" + s_iAN + sf_iAN);
+        test("null-2000000", "" + s_iAN + sf_iM);
+        test("null-820130816", "" + s_iAN + f_lM);
+        test("nullnull", "" + s_iAN + sf_oAN);
+        test("null25000000", "" + s_iAN + s_I);
+        test("-54-96.0", "" + f_bM + s_dM);
+        test("-54null", "" + f_bM + s_oNtS);
+        test("-54\u045176", "" + f_bM + f_strU);
+        test("-5492", "" + f_bM + sf_strU2);
+        test("-5451", "" + f_bM + sf_strU1);
+        test("-54null", "" + f_bM + s_iAN);
+        test("-54-54", "" + f_bM + f_bM);
+        test("-54-87.0", "" + f_bM + f_fM);
+        test("-54null", "" + f_bM + s_oAN);
+        test("-5419", "" + f_bM + f_str);
+        test("-54-41", "" + f_bM + sf_bM);
+        test("-54null", "" + f_bM + sf_IN);
+        test("-54T", "" + f_bM + s_c);
+        test("-54-42.0", "" + f_bM + sf_fM);
+        test("-5425", "" + f_bM + s_b);
+        test("-54null", "" + f_bM + f_oN);
+        test("-54-1410065408", "" + f_bM + s_lM);
+        test("-548.0", "" + f_bM + s_d);
+        test("-5455.0", "" + f_bM + s_f);
+        test("-5497000000", "" + f_bM + s_i);
+        test("-54-9900", "" + f_bM + f_sM);
+        test("-54935228928", "" + f_bM + s_l);
+        test("-54-8400", "" + f_bM + sf_sM);
+        test("-54C(82)", "" + f_bM + s_o);
+        test("-54null", "" + f_bM + sf_oNtS);
+        test("-54true", "" + f_bM + s_bl);
+        test("-543900", "" + f_bM + s_s);
+        test("-54null", "" + f_bM + sf_oN);
+        test("-5494000000", "" + f_bM + f_I);
+        test("-54null", "" + f_bM + f_IN);
+        test("-54true", "" + f_bM + sf_bl);
+        test("-545500", "" + f_bM + sf_s);
+        test("-54-2900", "" + f_bM + s_sM);
+        test("-54-194313216", "" + f_bM + sf_l);
+        test("-5412", "" + f_bM + s_strU1);
+        test("-54C(87)", "" + f_bM + sf_o);
+        test("-5491", "" + f_bM + s_strU2);
+        test("-5421", "" + f_bM + f_strU1);
+        test("-5418", "" + f_bM + f_strU2);
+        test("-54null", "" + f_bM + f_iAN);
+        test("-54null", "" + f_bM + s_oN);
+        test("-54\u045180", "" + f_bM + s_strU);
+        test("-54C", "" + f_bM + sf_c);
+        test("-5475", "" + f_bM + sf_str);
+        test("-54-43", "" + f_bM + s_bM);
+        test("-5480", "" + f_bM + sf_b);
+        test("-54null", "" + f_bM + s_IN);
+        test("-54-52.0", "" + f_bM + s_fM);
+        test("-5475000000", "" + f_bM + sf_i);
+        test("-5444", "" + f_bM + f_b);
+        test("-54-1705032704", "" + f_bM + sf_lM);
+        test("-54null", "" + f_bM + f_oAN);
+        test("-5483.0", "" + f_bM + f_d);
+        test("-54I", "" + f_bM + f_c);
+        test("-5494.0", "" + f_bM + f_f);
+        test("-5412.0", "" + f_bM + sf_d);
+        test("-54-99.0", "" + f_bM + f_dM);
+        test("-5417.0", "" + f_bM + sf_f);
+        test("-54-84.0", "" + f_bM + sf_dM);
+        test("-5458000000", "" + f_bM + f_i);
+        test("-54-55000000", "" + f_bM + f_iM);
+        test("-541460392448", "" + f_bM + f_l);
+        test("-54C(70)", "" + f_bM + f_o);
+        test("-54\u04511", "" + f_bM + sf_strU);
+        test("-548000", "" + f_bM + f_s);
+        test("-5418", "" + f_bM + s_str);
+        test("-54-1000000", "" + f_bM + s_iM);
+        test("-541000000", "" + f_bM + sf_I);
+        test("-54null", "" + f_bM + f_oNtS);
+        test("-54false", "" + f_bM + f_bl);
+        test("-54null", "" + f_bM + sf_iAN);
+        test("-54-2000000", "" + f_bM + sf_iM);
+        test("-54-820130816", "" + f_bM + f_lM);
+        test("-54null", "" + f_bM + sf_oAN);
+        test("-5425000000", "" + f_bM + s_I);
+        test("-87.0-96.0", "" + f_fM + s_dM);
+        test("-87.0null", "" + f_fM + s_oNtS);
+        test("-87.0\u045176", "" + f_fM + f_strU);
+        test("-87.092", "" + f_fM + sf_strU2);
+        test("-87.051", "" + f_fM + sf_strU1);
+        test("-87.0null", "" + f_fM + s_iAN);
+        test("-87.0-54", "" + f_fM + f_bM);
+        test("-87.0-87.0", "" + f_fM + f_fM);
+        test("-87.0null", "" + f_fM + s_oAN);
+        test("-87.019", "" + f_fM + f_str);
+        test("-87.0-41", "" + f_fM + sf_bM);
+        test("-87.0null", "" + f_fM + sf_IN);
+        test("-87.0T", "" + f_fM + s_c);
+        test("-87.0-42.0", "" + f_fM + sf_fM);
+        test("-87.025", "" + f_fM + s_b);
+        test("-87.0null", "" + f_fM + f_oN);
+        test("-87.0-1410065408", "" + f_fM + s_lM);
+        test("-87.08.0", "" + f_fM + s_d);
+        test("-87.055.0", "" + f_fM + s_f);
+        test("-87.097000000", "" + f_fM + s_i);
+        test("-87.0-9900", "" + f_fM + f_sM);
+        test("-87.0935228928", "" + f_fM + s_l);
+        test("-87.0-8400", "" + f_fM + sf_sM);
+        test("-87.0C(82)", "" + f_fM + s_o);
+        test("-87.0null", "" + f_fM + sf_oNtS);
+        test("-87.0true", "" + f_fM + s_bl);
+        test("-87.03900", "" + f_fM + s_s);
+        test("-87.0null", "" + f_fM + sf_oN);
+        test("-87.094000000", "" + f_fM + f_I);
+        test("-87.0null", "" + f_fM + f_IN);
+        test("-87.0true", "" + f_fM + sf_bl);
+        test("-87.05500", "" + f_fM + sf_s);
+        test("-87.0-2900", "" + f_fM + s_sM);
+        test("-87.0-194313216", "" + f_fM + sf_l);
+        test("-87.012", "" + f_fM + s_strU1);
+        test("-87.0C(87)", "" + f_fM + sf_o);
+        test("-87.091", "" + f_fM + s_strU2);
+        test("-87.021", "" + f_fM + f_strU1);
+        test("-87.018", "" + f_fM + f_strU2);
+        test("-87.0null", "" + f_fM + f_iAN);
+        test("-87.0null", "" + f_fM + s_oN);
+        test("-87.0\u045180", "" + f_fM + s_strU);
+        test("-87.0C", "" + f_fM + sf_c);
+        test("-87.075", "" + f_fM + sf_str);
+        test("-87.0-43", "" + f_fM + s_bM);
+        test("-87.080", "" + f_fM + sf_b);
+        test("-87.0null", "" + f_fM + s_IN);
+        test("-87.0-52.0", "" + f_fM + s_fM);
+        test("-87.075000000", "" + f_fM + sf_i);
+        test("-87.044", "" + f_fM + f_b);
+        test("-87.0-1705032704", "" + f_fM + sf_lM);
+        test("-87.0null", "" + f_fM + f_oAN);
+        test("-87.083.0", "" + f_fM + f_d);
+        test("-87.0I", "" + f_fM + f_c);
+        test("-87.094.0", "" + f_fM + f_f);
+        test("-87.012.0", "" + f_fM + sf_d);
+        test("-87.0-99.0", "" + f_fM + f_dM);
+        test("-87.017.0", "" + f_fM + sf_f);
+        test("-87.0-84.0", "" + f_fM + sf_dM);
+        test("-87.058000000", "" + f_fM + f_i);
+        test("-87.0-55000000", "" + f_fM + f_iM);
+        test("-87.01460392448", "" + f_fM + f_l);
+        test("-87.0C(70)", "" + f_fM + f_o);
+        test("-87.0\u04511", "" + f_fM + sf_strU);
+        test("-87.08000", "" + f_fM + f_s);
+        test("-87.018", "" + f_fM + s_str);
+        test("-87.0-1000000", "" + f_fM + s_iM);
+        test("-87.01000000", "" + f_fM + sf_I);
+        test("-87.0null", "" + f_fM + f_oNtS);
+        test("-87.0false", "" + f_fM + f_bl);
+        test("-87.0null", "" + f_fM + sf_iAN);
+        test("-87.0-2000000", "" + f_fM + sf_iM);
+        test("-87.0-820130816", "" + f_fM + f_lM);
+        test("-87.0null", "" + f_fM + sf_oAN);
+        test("-87.025000000", "" + f_fM + s_I);
+        test("null-96.0", "" + s_oAN + s_dM);
+        test("nullnull", "" + s_oAN + s_oNtS);
+        test("null\u045176", "" + s_oAN + f_strU);
+        test("null92", "" + s_oAN + sf_strU2);
+        test("null51", "" + s_oAN + sf_strU1);
+        test("nullnull", "" + s_oAN + s_iAN);
+        test("null-54", "" + s_oAN + f_bM);
+        test("null-87.0", "" + s_oAN + f_fM);
+        test("nullnull", "" + s_oAN + s_oAN);
+        test("null19", "" + s_oAN + f_str);
+        test("null-41", "" + s_oAN + sf_bM);
+        test("nullnull", "" + s_oAN + sf_IN);
+        test("nullT", "" + s_oAN + s_c);
+        test("null-42.0", "" + s_oAN + sf_fM);
+        test("null25", "" + s_oAN + s_b);
+        test("nullnull", "" + s_oAN + f_oN);
+        test("null-1410065408", "" + s_oAN + s_lM);
+        test("null8.0", "" + s_oAN + s_d);
+        test("null55.0", "" + s_oAN + s_f);
+        test("null97000000", "" + s_oAN + s_i);
+        test("null-9900", "" + s_oAN + f_sM);
+        test("null935228928", "" + s_oAN + s_l);
+        test("null-8400", "" + s_oAN + sf_sM);
+        test("nullC(82)", "" + s_oAN + s_o);
+        test("nullnull", "" + s_oAN + sf_oNtS);
+        test("nulltrue", "" + s_oAN + s_bl);
+        test("null3900", "" + s_oAN + s_s);
+        test("nullnull", "" + s_oAN + sf_oN);
+        test("null94000000", "" + s_oAN + f_I);
+        test("nullnull", "" + s_oAN + f_IN);
+        test("nulltrue", "" + s_oAN + sf_bl);
+        test("null5500", "" + s_oAN + sf_s);
+        test("null-2900", "" + s_oAN + s_sM);
+        test("null-194313216", "" + s_oAN + sf_l);
+        test("null12", "" + s_oAN + s_strU1);
+        test("nullC(87)", "" + s_oAN + sf_o);
+        test("null91", "" + s_oAN + s_strU2);
+        test("null21", "" + s_oAN + f_strU1);
+        test("null18", "" + s_oAN + f_strU2);
+        test("nullnull", "" + s_oAN + f_iAN);
+        test("nullnull", "" + s_oAN + s_oN);
+        test("null\u045180", "" + s_oAN + s_strU);
+        test("nullC", "" + s_oAN + sf_c);
+        test("null75", "" + s_oAN + sf_str);
+        test("null-43", "" + s_oAN + s_bM);
+        test("null80", "" + s_oAN + sf_b);
+        test("nullnull", "" + s_oAN + s_IN);
+        test("null-52.0", "" + s_oAN + s_fM);
+        test("null75000000", "" + s_oAN + sf_i);
+        test("null44", "" + s_oAN + f_b);
+        test("null-1705032704", "" + s_oAN + sf_lM);
+        test("nullnull", "" + s_oAN + f_oAN);
+        test("null83.0", "" + s_oAN + f_d);
+        test("nullI", "" + s_oAN + f_c);
+        test("null94.0", "" + s_oAN + f_f);
+        test("null12.0", "" + s_oAN + sf_d);
+        test("null-99.0", "" + s_oAN + f_dM);
+        test("null17.0", "" + s_oAN + sf_f);
+        test("null-84.0", "" + s_oAN + sf_dM);
+        test("null58000000", "" + s_oAN + f_i);
+        test("null-55000000", "" + s_oAN + f_iM);
+        test("null1460392448", "" + s_oAN + f_l);
+        test("nullC(70)", "" + s_oAN + f_o);
+        test("null\u04511", "" + s_oAN + sf_strU);
+        test("null8000", "" + s_oAN + f_s);
+        test("null18", "" + s_oAN + s_str);
+        test("null-1000000", "" + s_oAN + s_iM);
+        test("null1000000", "" + s_oAN + sf_I);
+        test("nullnull", "" + s_oAN + f_oNtS);
+        test("nullfalse", "" + s_oAN + f_bl);
+        test("nullnull", "" + s_oAN + sf_iAN);
+        test("null-2000000", "" + s_oAN + sf_iM);
+        test("null-820130816", "" + s_oAN + f_lM);
+        test("nullnull", "" + s_oAN + sf_oAN);
+        test("null25000000", "" + s_oAN + s_I);
+        test("19-96.0", "" + f_str + s_dM);
+        test("19null", "" + f_str + s_oNtS);
+        test("19\u045176", "" + f_str + f_strU);
+        test("1992", "" + f_str + sf_strU2);
+        test("1951", "" + f_str + sf_strU1);
+        test("19null", "" + f_str + s_iAN);
+        test("19-54", "" + f_str + f_bM);
+        test("19-87.0", "" + f_str + f_fM);
+        test("19null", "" + f_str + s_oAN);
+        test("1919", "" + f_str + f_str);
+        test("19-41", "" + f_str + sf_bM);
+        test("19null", "" + f_str + sf_IN);
+        test("19T", "" + f_str + s_c);
+        test("19-42.0", "" + f_str + sf_fM);
+        test("1925", "" + f_str + s_b);
+        test("19null", "" + f_str + f_oN);
+        test("19-1410065408", "" + f_str + s_lM);
+        test("198.0", "" + f_str + s_d);
+        test("1955.0", "" + f_str + s_f);
+        test("1997000000", "" + f_str + s_i);
+        test("19-9900", "" + f_str + f_sM);
+        test("19935228928", "" + f_str + s_l);
+        test("19-8400", "" + f_str + sf_sM);
+        test("19C(82)", "" + f_str + s_o);
+        test("19null", "" + f_str + sf_oNtS);
+        test("19true", "" + f_str + s_bl);
+        test("193900", "" + f_str + s_s);
+        test("19null", "" + f_str + sf_oN);
+        test("1994000000", "" + f_str + f_I);
+        test("19null", "" + f_str + f_IN);
+        test("19true", "" + f_str + sf_bl);
+        test("195500", "" + f_str + sf_s);
+        test("19-2900", "" + f_str + s_sM);
+        test("19-194313216", "" + f_str + sf_l);
+        test("1912", "" + f_str + s_strU1);
+        test("19C(87)", "" + f_str + sf_o);
+        test("1991", "" + f_str + s_strU2);
+        test("1921", "" + f_str + f_strU1);
+        test("1918", "" + f_str + f_strU2);
+        test("19null", "" + f_str + f_iAN);
+        test("19null", "" + f_str + s_oN);
+        test("19\u045180", "" + f_str + s_strU);
+        test("19C", "" + f_str + sf_c);
+        test("1975", "" + f_str + sf_str);
+        test("19-43", "" + f_str + s_bM);
+        test("1980", "" + f_str + sf_b);
+        test("19null", "" + f_str + s_IN);
+        test("19-52.0", "" + f_str + s_fM);
+        test("1975000000", "" + f_str + sf_i);
+        test("1944", "" + f_str + f_b);
+        test("19-1705032704", "" + f_str + sf_lM);
+        test("19null", "" + f_str + f_oAN);
+        test("1983.0", "" + f_str + f_d);
+        test("19I", "" + f_str + f_c);
+        test("1994.0", "" + f_str + f_f);
+        test("1912.0", "" + f_str + sf_d);
+        test("19-99.0", "" + f_str + f_dM);
+        test("1917.0", "" + f_str + sf_f);
+        test("19-84.0", "" + f_str + sf_dM);
+        test("1958000000", "" + f_str + f_i);
+        test("19-55000000", "" + f_str + f_iM);
+        test("191460392448", "" + f_str + f_l);
+        test("19C(70)", "" + f_str + f_o);
+        test("19\u04511", "" + f_str + sf_strU);
+        test("198000", "" + f_str + f_s);
+        test("1918", "" + f_str + s_str);
+        test("19-1000000", "" + f_str + s_iM);
+        test("191000000", "" + f_str + sf_I);
+        test("19null", "" + f_str + f_oNtS);
+        test("19false", "" + f_str + f_bl);
+        test("19null", "" + f_str + sf_iAN);
+        test("19-2000000", "" + f_str + sf_iM);
+        test("19-820130816", "" + f_str + f_lM);
+        test("19null", "" + f_str + sf_oAN);
+        test("1925000000", "" + f_str + s_I);
+        test("-41-96.0", "" + sf_bM + s_dM);
+        test("-41null", "" + sf_bM + s_oNtS);
+        test("-41\u045176", "" + sf_bM + f_strU);
+        test("-4192", "" + sf_bM + sf_strU2);
+        test("-4151", "" + sf_bM + sf_strU1);
+        test("-41null", "" + sf_bM + s_iAN);
+        test("-41-54", "" + sf_bM + f_bM);
+        test("-41-87.0", "" + sf_bM + f_fM);
+        test("-41null", "" + sf_bM + s_oAN);
+        test("-4119", "" + sf_bM + f_str);
+        test("-41-41", "" + sf_bM + sf_bM);
+        test("-41null", "" + sf_bM + sf_IN);
+        test("-41T", "" + sf_bM + s_c);
+        test("-41-42.0", "" + sf_bM + sf_fM);
+        test("-4125", "" + sf_bM + s_b);
+        test("-41null", "" + sf_bM + f_oN);
+        test("-41-1410065408", "" + sf_bM + s_lM);
+        test("-418.0", "" + sf_bM + s_d);
+        test("-4155.0", "" + sf_bM + s_f);
+        test("-4197000000", "" + sf_bM + s_i);
+        test("-41-9900", "" + sf_bM + f_sM);
+        test("-41935228928", "" + sf_bM + s_l);
+        test("-41-8400", "" + sf_bM + sf_sM);
+        test("-41C(82)", "" + sf_bM + s_o);
+        test("-41null", "" + sf_bM + sf_oNtS);
+        test("-41true", "" + sf_bM + s_bl);
+        test("-413900", "" + sf_bM + s_s);
+        test("-41null", "" + sf_bM + sf_oN);
+        test("-4194000000", "" + sf_bM + f_I);
+        test("-41null", "" + sf_bM + f_IN);
+        test("-41true", "" + sf_bM + sf_bl);
+        test("-415500", "" + sf_bM + sf_s);
+        test("-41-2900", "" + sf_bM + s_sM);
+        test("-41-194313216", "" + sf_bM + sf_l);
+        test("-4112", "" + sf_bM + s_strU1);
+        test("-41C(87)", "" + sf_bM + sf_o);
+        test("-4191", "" + sf_bM + s_strU2);
+        test("-4121", "" + sf_bM + f_strU1);
+        test("-4118", "" + sf_bM + f_strU2);
+        test("-41null", "" + sf_bM + f_iAN);
+        test("-41null", "" + sf_bM + s_oN);
+        test("-41\u045180", "" + sf_bM + s_strU);
+        test("-41C", "" + sf_bM + sf_c);
+        test("-4175", "" + sf_bM + sf_str);
+        test("-41-43", "" + sf_bM + s_bM);
+        test("-4180", "" + sf_bM + sf_b);
+        test("-41null", "" + sf_bM + s_IN);
+        test("-41-52.0", "" + sf_bM + s_fM);
+        test("-4175000000", "" + sf_bM + sf_i);
+        test("-4144", "" + sf_bM + f_b);
+        test("-41-1705032704", "" + sf_bM + sf_lM);
+        test("-41null", "" + sf_bM + f_oAN);
+        test("-4183.0", "" + sf_bM + f_d);
+        test("-41I", "" + sf_bM + f_c);
+        test("-4194.0", "" + sf_bM + f_f);
+        test("-4112.0", "" + sf_bM + sf_d);
+        test("-41-99.0", "" + sf_bM + f_dM);
+        test("-4117.0", "" + sf_bM + sf_f);
+        test("-41-84.0", "" + sf_bM + sf_dM);
+        test("-4158000000", "" + sf_bM + f_i);
+        test("-41-55000000", "" + sf_bM + f_iM);
+        test("-411460392448", "" + sf_bM + f_l);
+        test("-41C(70)", "" + sf_bM + f_o);
+        test("-41\u04511", "" + sf_bM + sf_strU);
+        test("-418000", "" + sf_bM + f_s);
+        test("-4118", "" + sf_bM + s_str);
+        test("-41-1000000", "" + sf_bM + s_iM);
+        test("-411000000", "" + sf_bM + sf_I);
+        test("-41null", "" + sf_bM + f_oNtS);
+        test("-41false", "" + sf_bM + f_bl);
+        test("-41null", "" + sf_bM + sf_iAN);
+        test("-41-2000000", "" + sf_bM + sf_iM);
+        test("-41-820130816", "" + sf_bM + f_lM);
+        test("-41null", "" + sf_bM + sf_oAN);
+        test("-4125000000", "" + sf_bM + s_I);
+        test("null-96.0", "" + sf_IN + s_dM);
+        test("nullnull", "" + sf_IN + s_oNtS);
+        test("null\u045176", "" + sf_IN + f_strU);
+        test("null92", "" + sf_IN + sf_strU2);
+        test("null51", "" + sf_IN + sf_strU1);
+        test("nullnull", "" + sf_IN + s_iAN);
+        test("null-54", "" + sf_IN + f_bM);
+        test("null-87.0", "" + sf_IN + f_fM);
+        test("nullnull", "" + sf_IN + s_oAN);
+        test("null19", "" + sf_IN + f_str);
+        test("null-41", "" + sf_IN + sf_bM);
+        test("nullnull", "" + sf_IN + sf_IN);
+        test("nullT", "" + sf_IN + s_c);
+        test("null-42.0", "" + sf_IN + sf_fM);
+        test("null25", "" + sf_IN + s_b);
+        test("nullnull", "" + sf_IN + f_oN);
+        test("null-1410065408", "" + sf_IN + s_lM);
+        test("null8.0", "" + sf_IN + s_d);
+        test("null55.0", "" + sf_IN + s_f);
+        test("null97000000", "" + sf_IN + s_i);
+        test("null-9900", "" + sf_IN + f_sM);
+        test("null935228928", "" + sf_IN + s_l);
+        test("null-8400", "" + sf_IN + sf_sM);
+        test("nullC(82)", "" + sf_IN + s_o);
+        test("nullnull", "" + sf_IN + sf_oNtS);
+        test("nulltrue", "" + sf_IN + s_bl);
+        test("null3900", "" + sf_IN + s_s);
+        test("nullnull", "" + sf_IN + sf_oN);
+        test("null94000000", "" + sf_IN + f_I);
+        test("nullnull", "" + sf_IN + f_IN);
+        test("nulltrue", "" + sf_IN + sf_bl);
+        test("null5500", "" + sf_IN + sf_s);
+        test("null-2900", "" + sf_IN + s_sM);
+        test("null-194313216", "" + sf_IN + sf_l);
+        test("null12", "" + sf_IN + s_strU1);
+        test("nullC(87)", "" + sf_IN + sf_o);
+        test("null91", "" + sf_IN + s_strU2);
+        test("null21", "" + sf_IN + f_strU1);
+        test("null18", "" + sf_IN + f_strU2);
+        test("nullnull", "" + sf_IN + f_iAN);
+        test("nullnull", "" + sf_IN + s_oN);
+        test("null\u045180", "" + sf_IN + s_strU);
+        test("nullC", "" + sf_IN + sf_c);
+        test("null75", "" + sf_IN + sf_str);
+        test("null-43", "" + sf_IN + s_bM);
+        test("null80", "" + sf_IN + sf_b);
+        test("nullnull", "" + sf_IN + s_IN);
+        test("null-52.0", "" + sf_IN + s_fM);
+        test("null75000000", "" + sf_IN + sf_i);
+        test("null44", "" + sf_IN + f_b);
+        test("null-1705032704", "" + sf_IN + sf_lM);
+        test("nullnull", "" + sf_IN + f_oAN);
+        test("null83.0", "" + sf_IN + f_d);
+        test("nullI", "" + sf_IN + f_c);
+        test("null94.0", "" + sf_IN + f_f);
+        test("null12.0", "" + sf_IN + sf_d);
+        test("null-99.0", "" + sf_IN + f_dM);
+        test("null17.0", "" + sf_IN + sf_f);
+        test("null-84.0", "" + sf_IN + sf_dM);
+        test("null58000000", "" + sf_IN + f_i);
+        test("null-55000000", "" + sf_IN + f_iM);
+        test("null1460392448", "" + sf_IN + f_l);
+        test("nullC(70)", "" + sf_IN + f_o);
+        test("null\u04511", "" + sf_IN + sf_strU);
+        test("null8000", "" + sf_IN + f_s);
+        test("null18", "" + sf_IN + s_str);
+        test("null-1000000", "" + sf_IN + s_iM);
+        test("null1000000", "" + sf_IN + sf_I);
+        test("nullnull", "" + sf_IN + f_oNtS);
+        test("nullfalse", "" + sf_IN + f_bl);
+        test("nullnull", "" + sf_IN + sf_iAN);
+        test("null-2000000", "" + sf_IN + sf_iM);
+        test("null-820130816", "" + sf_IN + f_lM);
+        test("nullnull", "" + sf_IN + sf_oAN);
+        test("null25000000", "" + sf_IN + s_I);
+        test("T-96.0", "" + s_c + s_dM);
+        test("Tnull", "" + s_c + s_oNtS);
+        test("T\u045176", "" + s_c + f_strU);
+        test("T92", "" + s_c + sf_strU2);
+        test("T51", "" + s_c + sf_strU1);
+        test("Tnull", "" + s_c + s_iAN);
+        test("T-54", "" + s_c + f_bM);
+        test("T-87.0", "" + s_c + f_fM);
+        test("Tnull", "" + s_c + s_oAN);
+        test("T19", "" + s_c + f_str);
+        test("T-41", "" + s_c + sf_bM);
+        test("Tnull", "" + s_c + sf_IN);
+        test("TT", "" + s_c + s_c);
+        test("T-42.0", "" + s_c + sf_fM);
+        test("T25", "" + s_c + s_b);
+        test("Tnull", "" + s_c + f_oN);
+        test("T-1410065408", "" + s_c + s_lM);
+        test("T8.0", "" + s_c + s_d);
+        test("T55.0", "" + s_c + s_f);
+        test("T97000000", "" + s_c + s_i);
+        test("T-9900", "" + s_c + f_sM);
+        test("T935228928", "" + s_c + s_l);
+        test("T-8400", "" + s_c + sf_sM);
+        test("TC(82)", "" + s_c + s_o);
+        test("Tnull", "" + s_c + sf_oNtS);
+    }
+
+    public void run1() {
+        test("Ttrue", "" + s_c + s_bl);
+        test("T3900", "" + s_c + s_s);
+        test("Tnull", "" + s_c + sf_oN);
+        test("T94000000", "" + s_c + f_I);
+        test("Tnull", "" + s_c + f_IN);
+        test("Ttrue", "" + s_c + sf_bl);
+        test("T5500", "" + s_c + sf_s);
+        test("T-2900", "" + s_c + s_sM);
+        test("T-194313216", "" + s_c + sf_l);
+        test("T12", "" + s_c + s_strU1);
+        test("TC(87)", "" + s_c + sf_o);
+        test("T91", "" + s_c + s_strU2);
+        test("T21", "" + s_c + f_strU1);
+        test("T18", "" + s_c + f_strU2);
+        test("Tnull", "" + s_c + f_iAN);
+        test("Tnull", "" + s_c + s_oN);
+        test("T\u045180", "" + s_c + s_strU);
+        test("TC", "" + s_c + sf_c);
+        test("T75", "" + s_c + sf_str);
+        test("T-43", "" + s_c + s_bM);
+        test("T80", "" + s_c + sf_b);
+        test("Tnull", "" + s_c + s_IN);
+        test("T-52.0", "" + s_c + s_fM);
+        test("T75000000", "" + s_c + sf_i);
+        test("T44", "" + s_c + f_b);
+        test("T-1705032704", "" + s_c + sf_lM);
+        test("Tnull", "" + s_c + f_oAN);
+        test("T83.0", "" + s_c + f_d);
+        test("TI", "" + s_c + f_c);
+        test("T94.0", "" + s_c + f_f);
+        test("T12.0", "" + s_c + sf_d);
+        test("T-99.0", "" + s_c + f_dM);
+        test("T17.0", "" + s_c + sf_f);
+        test("T-84.0", "" + s_c + sf_dM);
+        test("T58000000", "" + s_c + f_i);
+        test("T-55000000", "" + s_c + f_iM);
+        test("T1460392448", "" + s_c + f_l);
+        test("TC(70)", "" + s_c + f_o);
+        test("T\u04511", "" + s_c + sf_strU);
+        test("T8000", "" + s_c + f_s);
+        test("T18", "" + s_c + s_str);
+        test("T-1000000", "" + s_c + s_iM);
+        test("T1000000", "" + s_c + sf_I);
+        test("Tnull", "" + s_c + f_oNtS);
+        test("Tfalse", "" + s_c + f_bl);
+        test("Tnull", "" + s_c + sf_iAN);
+        test("T-2000000", "" + s_c + sf_iM);
+        test("T-820130816", "" + s_c + f_lM);
+        test("Tnull", "" + s_c + sf_oAN);
+        test("T25000000", "" + s_c + s_I);
+        test("-42.0-96.0", "" + sf_fM + s_dM);
+        test("-42.0null", "" + sf_fM + s_oNtS);
+        test("-42.0\u045176", "" + sf_fM + f_strU);
+        test("-42.092", "" + sf_fM + sf_strU2);
+        test("-42.051", "" + sf_fM + sf_strU1);
+        test("-42.0null", "" + sf_fM + s_iAN);
+        test("-42.0-54", "" + sf_fM + f_bM);
+        test("-42.0-87.0", "" + sf_fM + f_fM);
+        test("-42.0null", "" + sf_fM + s_oAN);
+        test("-42.019", "" + sf_fM + f_str);
+        test("-42.0-41", "" + sf_fM + sf_bM);
+        test("-42.0null", "" + sf_fM + sf_IN);
+        test("-42.0T", "" + sf_fM + s_c);
+        test("-42.0-42.0", "" + sf_fM + sf_fM);
+        test("-42.025", "" + sf_fM + s_b);
+        test("-42.0null", "" + sf_fM + f_oN);
+        test("-42.0-1410065408", "" + sf_fM + s_lM);
+        test("-42.08.0", "" + sf_fM + s_d);
+        test("-42.055.0", "" + sf_fM + s_f);
+        test("-42.097000000", "" + sf_fM + s_i);
+        test("-42.0-9900", "" + sf_fM + f_sM);
+        test("-42.0935228928", "" + sf_fM + s_l);
+        test("-42.0-8400", "" + sf_fM + sf_sM);
+        test("-42.0C(82)", "" + sf_fM + s_o);
+        test("-42.0null", "" + sf_fM + sf_oNtS);
+        test("-42.0true", "" + sf_fM + s_bl);
+        test("-42.03900", "" + sf_fM + s_s);
+        test("-42.0null", "" + sf_fM + sf_oN);
+        test("-42.094000000", "" + sf_fM + f_I);
+        test("-42.0null", "" + sf_fM + f_IN);
+        test("-42.0true", "" + sf_fM + sf_bl);
+        test("-42.05500", "" + sf_fM + sf_s);
+        test("-42.0-2900", "" + sf_fM + s_sM);
+        test("-42.0-194313216", "" + sf_fM + sf_l);
+        test("-42.012", "" + sf_fM + s_strU1);
+        test("-42.0C(87)", "" + sf_fM + sf_o);
+        test("-42.091", "" + sf_fM + s_strU2);
+        test("-42.021", "" + sf_fM + f_strU1);
+        test("-42.018", "" + sf_fM + f_strU2);
+        test("-42.0null", "" + sf_fM + f_iAN);
+        test("-42.0null", "" + sf_fM + s_oN);
+        test("-42.0\u045180", "" + sf_fM + s_strU);
+        test("-42.0C", "" + sf_fM + sf_c);
+        test("-42.075", "" + sf_fM + sf_str);
+        test("-42.0-43", "" + sf_fM + s_bM);
+        test("-42.080", "" + sf_fM + sf_b);
+        test("-42.0null", "" + sf_fM + s_IN);
+        test("-42.0-52.0", "" + sf_fM + s_fM);
+        test("-42.075000000", "" + sf_fM + sf_i);
+        test("-42.044", "" + sf_fM + f_b);
+        test("-42.0-1705032704", "" + sf_fM + sf_lM);
+        test("-42.0null", "" + sf_fM + f_oAN);
+        test("-42.083.0", "" + sf_fM + f_d);
+        test("-42.0I", "" + sf_fM + f_c);
+        test("-42.094.0", "" + sf_fM + f_f);
+        test("-42.012.0", "" + sf_fM + sf_d);
+        test("-42.0-99.0", "" + sf_fM + f_dM);
+        test("-42.017.0", "" + sf_fM + sf_f);
+        test("-42.0-84.0", "" + sf_fM + sf_dM);
+        test("-42.058000000", "" + sf_fM + f_i);
+        test("-42.0-55000000", "" + sf_fM + f_iM);
+        test("-42.01460392448", "" + sf_fM + f_l);
+        test("-42.0C(70)", "" + sf_fM + f_o);
+        test("-42.0\u04511", "" + sf_fM + sf_strU);
+        test("-42.08000", "" + sf_fM + f_s);
+        test("-42.018", "" + sf_fM + s_str);
+        test("-42.0-1000000", "" + sf_fM + s_iM);
+        test("-42.01000000", "" + sf_fM + sf_I);
+        test("-42.0null", "" + sf_fM + f_oNtS);
+        test("-42.0false", "" + sf_fM + f_bl);
+        test("-42.0null", "" + sf_fM + sf_iAN);
+        test("-42.0-2000000", "" + sf_fM + sf_iM);
+        test("-42.0-820130816", "" + sf_fM + f_lM);
+        test("-42.0null", "" + sf_fM + sf_oAN);
+        test("-42.025000000", "" + sf_fM + s_I);
+        test("25-96.0", "" + s_b + s_dM);
+        test("25null", "" + s_b + s_oNtS);
+        test("25\u045176", "" + s_b + f_strU);
+        test("2592", "" + s_b + sf_strU2);
+        test("2551", "" + s_b + sf_strU1);
+        test("25null", "" + s_b + s_iAN);
+        test("25-54", "" + s_b + f_bM);
+        test("25-87.0", "" + s_b + f_fM);
+        test("25null", "" + s_b + s_oAN);
+        test("2519", "" + s_b + f_str);
+        test("25-41", "" + s_b + sf_bM);
+        test("25null", "" + s_b + sf_IN);
+        test("25T", "" + s_b + s_c);
+        test("25-42.0", "" + s_b + sf_fM);
+        test("2525", "" + s_b + s_b);
+        test("25null", "" + s_b + f_oN);
+        test("25-1410065408", "" + s_b + s_lM);
+        test("258.0", "" + s_b + s_d);
+        test("2555.0", "" + s_b + s_f);
+        test("2597000000", "" + s_b + s_i);
+        test("25-9900", "" + s_b + f_sM);
+        test("25935228928", "" + s_b + s_l);
+        test("25-8400", "" + s_b + sf_sM);
+        test("25C(82)", "" + s_b + s_o);
+        test("25null", "" + s_b + sf_oNtS);
+        test("25true", "" + s_b + s_bl);
+        test("253900", "" + s_b + s_s);
+        test("25null", "" + s_b + sf_oN);
+        test("2594000000", "" + s_b + f_I);
+        test("25null", "" + s_b + f_IN);
+        test("25true", "" + s_b + sf_bl);
+        test("255500", "" + s_b + sf_s);
+        test("25-2900", "" + s_b + s_sM);
+        test("25-194313216", "" + s_b + sf_l);
+        test("2512", "" + s_b + s_strU1);
+        test("25C(87)", "" + s_b + sf_o);
+        test("2591", "" + s_b + s_strU2);
+        test("2521", "" + s_b + f_strU1);
+        test("2518", "" + s_b + f_strU2);
+        test("25null", "" + s_b + f_iAN);
+        test("25null", "" + s_b + s_oN);
+        test("25\u045180", "" + s_b + s_strU);
+        test("25C", "" + s_b + sf_c);
+        test("2575", "" + s_b + sf_str);
+        test("25-43", "" + s_b + s_bM);
+        test("2580", "" + s_b + sf_b);
+        test("25null", "" + s_b + s_IN);
+        test("25-52.0", "" + s_b + s_fM);
+        test("2575000000", "" + s_b + sf_i);
+        test("2544", "" + s_b + f_b);
+        test("25-1705032704", "" + s_b + sf_lM);
+        test("25null", "" + s_b + f_oAN);
+        test("2583.0", "" + s_b + f_d);
+        test("25I", "" + s_b + f_c);
+        test("2594.0", "" + s_b + f_f);
+        test("2512.0", "" + s_b + sf_d);
+        test("25-99.0", "" + s_b + f_dM);
+        test("2517.0", "" + s_b + sf_f);
+        test("25-84.0", "" + s_b + sf_dM);
+        test("2558000000", "" + s_b + f_i);
+        test("25-55000000", "" + s_b + f_iM);
+        test("251460392448", "" + s_b + f_l);
+        test("25C(70)", "" + s_b + f_o);
+        test("25\u04511", "" + s_b + sf_strU);
+        test("258000", "" + s_b + f_s);
+        test("2518", "" + s_b + s_str);
+        test("25-1000000", "" + s_b + s_iM);
+        test("251000000", "" + s_b + sf_I);
+        test("25null", "" + s_b + f_oNtS);
+        test("25false", "" + s_b + f_bl);
+        test("25null", "" + s_b + sf_iAN);
+        test("25-2000000", "" + s_b + sf_iM);
+        test("25-820130816", "" + s_b + f_lM);
+        test("25null", "" + s_b + sf_oAN);
+        test("2525000000", "" + s_b + s_I);
+        test("null-96.0", "" + f_oN + s_dM);
+        test("nullnull", "" + f_oN + s_oNtS);
+        test("null\u045176", "" + f_oN + f_strU);
+        test("null92", "" + f_oN + sf_strU2);
+        test("null51", "" + f_oN + sf_strU1);
+        test("nullnull", "" + f_oN + s_iAN);
+        test("null-54", "" + f_oN + f_bM);
+        test("null-87.0", "" + f_oN + f_fM);
+        test("nullnull", "" + f_oN + s_oAN);
+        test("null19", "" + f_oN + f_str);
+        test("null-41", "" + f_oN + sf_bM);
+        test("nullnull", "" + f_oN + sf_IN);
+        test("nullT", "" + f_oN + s_c);
+        test("null-42.0", "" + f_oN + sf_fM);
+        test("null25", "" + f_oN + s_b);
+        test("nullnull", "" + f_oN + f_oN);
+        test("null-1410065408", "" + f_oN + s_lM);
+        test("null8.0", "" + f_oN + s_d);
+        test("null55.0", "" + f_oN + s_f);
+        test("null97000000", "" + f_oN + s_i);
+        test("null-9900", "" + f_oN + f_sM);
+        test("null935228928", "" + f_oN + s_l);
+        test("null-8400", "" + f_oN + sf_sM);
+        test("nullC(82)", "" + f_oN + s_o);
+        test("nullnull", "" + f_oN + sf_oNtS);
+        test("nulltrue", "" + f_oN + s_bl);
+        test("null3900", "" + f_oN + s_s);
+        test("nullnull", "" + f_oN + sf_oN);
+        test("null94000000", "" + f_oN + f_I);
+        test("nullnull", "" + f_oN + f_IN);
+        test("nulltrue", "" + f_oN + sf_bl);
+        test("null5500", "" + f_oN + sf_s);
+        test("null-2900", "" + f_oN + s_sM);
+        test("null-194313216", "" + f_oN + sf_l);
+        test("null12", "" + f_oN + s_strU1);
+        test("nullC(87)", "" + f_oN + sf_o);
+        test("null91", "" + f_oN + s_strU2);
+        test("null21", "" + f_oN + f_strU1);
+        test("null18", "" + f_oN + f_strU2);
+        test("nullnull", "" + f_oN + f_iAN);
+        test("nullnull", "" + f_oN + s_oN);
+        test("null\u045180", "" + f_oN + s_strU);
+        test("nullC", "" + f_oN + sf_c);
+        test("null75", "" + f_oN + sf_str);
+        test("null-43", "" + f_oN + s_bM);
+        test("null80", "" + f_oN + sf_b);
+        test("nullnull", "" + f_oN + s_IN);
+        test("null-52.0", "" + f_oN + s_fM);
+        test("null75000000", "" + f_oN + sf_i);
+        test("null44", "" + f_oN + f_b);
+        test("null-1705032704", "" + f_oN + sf_lM);
+        test("nullnull", "" + f_oN + f_oAN);
+        test("null83.0", "" + f_oN + f_d);
+        test("nullI", "" + f_oN + f_c);
+        test("null94.0", "" + f_oN + f_f);
+        test("null12.0", "" + f_oN + sf_d);
+        test("null-99.0", "" + f_oN + f_dM);
+        test("null17.0", "" + f_oN + sf_f);
+        test("null-84.0", "" + f_oN + sf_dM);
+        test("null58000000", "" + f_oN + f_i);
+        test("null-55000000", "" + f_oN + f_iM);
+        test("null1460392448", "" + f_oN + f_l);
+        test("nullC(70)", "" + f_oN + f_o);
+        test("null\u04511", "" + f_oN + sf_strU);
+        test("null8000", "" + f_oN + f_s);
+        test("null18", "" + f_oN + s_str);
+        test("null-1000000", "" + f_oN + s_iM);
+        test("null1000000", "" + f_oN + sf_I);
+        test("nullnull", "" + f_oN + f_oNtS);
+        test("nullfalse", "" + f_oN + f_bl);
+        test("nullnull", "" + f_oN + sf_iAN);
+        test("null-2000000", "" + f_oN + sf_iM);
+        test("null-820130816", "" + f_oN + f_lM);
+        test("nullnull", "" + f_oN + sf_oAN);
+        test("null25000000", "" + f_oN + s_I);
+        test("-1410065408-96.0", "" + s_lM + s_dM);
+        test("-1410065408null", "" + s_lM + s_oNtS);
+        test("-1410065408\u045176", "" + s_lM + f_strU);
+        test("-141006540892", "" + s_lM + sf_strU2);
+        test("-141006540851", "" + s_lM + sf_strU1);
+        test("-1410065408null", "" + s_lM + s_iAN);
+        test("-1410065408-54", "" + s_lM + f_bM);
+        test("-1410065408-87.0", "" + s_lM + f_fM);
+        test("-1410065408null", "" + s_lM + s_oAN);
+        test("-141006540819", "" + s_lM + f_str);
+        test("-1410065408-41", "" + s_lM + sf_bM);
+        test("-1410065408null", "" + s_lM + sf_IN);
+        test("-1410065408T", "" + s_lM + s_c);
+        test("-1410065408-42.0", "" + s_lM + sf_fM);
+        test("-141006540825", "" + s_lM + s_b);
+        test("-1410065408null", "" + s_lM + f_oN);
+        test("-1410065408-1410065408", "" + s_lM + s_lM);
+        test("-14100654088.0", "" + s_lM + s_d);
+        test("-141006540855.0", "" + s_lM + s_f);
+        test("-141006540897000000", "" + s_lM + s_i);
+        test("-1410065408-9900", "" + s_lM + f_sM);
+        test("-1410065408935228928", "" + s_lM + s_l);
+        test("-1410065408-8400", "" + s_lM + sf_sM);
+        test("-1410065408C(82)", "" + s_lM + s_o);
+        test("-1410065408null", "" + s_lM + sf_oNtS);
+        test("-1410065408true", "" + s_lM + s_bl);
+        test("-14100654083900", "" + s_lM + s_s);
+        test("-1410065408null", "" + s_lM + sf_oN);
+        test("-141006540894000000", "" + s_lM + f_I);
+        test("-1410065408null", "" + s_lM + f_IN);
+        test("-1410065408true", "" + s_lM + sf_bl);
+        test("-14100654085500", "" + s_lM + sf_s);
+        test("-1410065408-2900", "" + s_lM + s_sM);
+        test("-1410065408-194313216", "" + s_lM + sf_l);
+        test("-141006540812", "" + s_lM + s_strU1);
+        test("-1410065408C(87)", "" + s_lM + sf_o);
+        test("-141006540891", "" + s_lM + s_strU2);
+        test("-141006540821", "" + s_lM + f_strU1);
+        test("-141006540818", "" + s_lM + f_strU2);
+        test("-1410065408null", "" + s_lM + f_iAN);
+        test("-1410065408null", "" + s_lM + s_oN);
+        test("-1410065408\u045180", "" + s_lM + s_strU);
+        test("-1410065408C", "" + s_lM + sf_c);
+        test("-141006540875", "" + s_lM + sf_str);
+        test("-1410065408-43", "" + s_lM + s_bM);
+        test("-141006540880", "" + s_lM + sf_b);
+        test("-1410065408null", "" + s_lM + s_IN);
+        test("-1410065408-52.0", "" + s_lM + s_fM);
+        test("-141006540875000000", "" + s_lM + sf_i);
+        test("-141006540844", "" + s_lM + f_b);
+        test("-1410065408-1705032704", "" + s_lM + sf_lM);
+        test("-1410065408null", "" + s_lM + f_oAN);
+        test("-141006540883.0", "" + s_lM + f_d);
+        test("-1410065408I", "" + s_lM + f_c);
+        test("-141006540894.0", "" + s_lM + f_f);
+        test("-141006540812.0", "" + s_lM + sf_d);
+        test("-1410065408-99.0", "" + s_lM + f_dM);
+        test("-141006540817.0", "" + s_lM + sf_f);
+        test("-1410065408-84.0", "" + s_lM + sf_dM);
+        test("-141006540858000000", "" + s_lM + f_i);
+        test("-1410065408-55000000", "" + s_lM + f_iM);
+        test("-14100654081460392448", "" + s_lM + f_l);
+        test("-1410065408C(70)", "" + s_lM + f_o);
+        test("-1410065408\u04511", "" + s_lM + sf_strU);
+        test("-14100654088000", "" + s_lM + f_s);
+        test("-141006540818", "" + s_lM + s_str);
+        test("-1410065408-1000000", "" + s_lM + s_iM);
+        test("-14100654081000000", "" + s_lM + sf_I);
+        test("-1410065408null", "" + s_lM + f_oNtS);
+        test("-1410065408false", "" + s_lM + f_bl);
+        test("-1410065408null", "" + s_lM + sf_iAN);
+        test("-1410065408-2000000", "" + s_lM + sf_iM);
+        test("-1410065408-820130816", "" + s_lM + f_lM);
+        test("-1410065408null", "" + s_lM + sf_oAN);
+        test("-141006540825000000", "" + s_lM + s_I);
+        test("8.0-96.0", "" + s_d + s_dM);
+        test("8.0null", "" + s_d + s_oNtS);
+        test("8.0\u045176", "" + s_d + f_strU);
+        test("8.092", "" + s_d + sf_strU2);
+        test("8.051", "" + s_d + sf_strU1);
+        test("8.0null", "" + s_d + s_iAN);
+        test("8.0-54", "" + s_d + f_bM);
+        test("8.0-87.0", "" + s_d + f_fM);
+        test("8.0null", "" + s_d + s_oAN);
+        test("8.019", "" + s_d + f_str);
+        test("8.0-41", "" + s_d + sf_bM);
+        test("8.0null", "" + s_d + sf_IN);
+        test("8.0T", "" + s_d + s_c);
+        test("8.0-42.0", "" + s_d + sf_fM);
+        test("8.025", "" + s_d + s_b);
+        test("8.0null", "" + s_d + f_oN);
+        test("8.0-1410065408", "" + s_d + s_lM);
+        test("8.08.0", "" + s_d + s_d);
+        test("8.055.0", "" + s_d + s_f);
+        test("8.097000000", "" + s_d + s_i);
+        test("8.0-9900", "" + s_d + f_sM);
+        test("8.0935228928", "" + s_d + s_l);
+        test("8.0-8400", "" + s_d + sf_sM);
+        test("8.0C(82)", "" + s_d + s_o);
+        test("8.0null", "" + s_d + sf_oNtS);
+        test("8.0true", "" + s_d + s_bl);
+        test("8.03900", "" + s_d + s_s);
+        test("8.0null", "" + s_d + sf_oN);
+        test("8.094000000", "" + s_d + f_I);
+        test("8.0null", "" + s_d + f_IN);
+        test("8.0true", "" + s_d + sf_bl);
+        test("8.05500", "" + s_d + sf_s);
+        test("8.0-2900", "" + s_d + s_sM);
+        test("8.0-194313216", "" + s_d + sf_l);
+        test("8.012", "" + s_d + s_strU1);
+        test("8.0C(87)", "" + s_d + sf_o);
+        test("8.091", "" + s_d + s_strU2);
+        test("8.021", "" + s_d + f_strU1);
+        test("8.018", "" + s_d + f_strU2);
+        test("8.0null", "" + s_d + f_iAN);
+        test("8.0null", "" + s_d + s_oN);
+        test("8.0\u045180", "" + s_d + s_strU);
+        test("8.0C", "" + s_d + sf_c);
+        test("8.075", "" + s_d + sf_str);
+        test("8.0-43", "" + s_d + s_bM);
+        test("8.080", "" + s_d + sf_b);
+        test("8.0null", "" + s_d + s_IN);
+        test("8.0-52.0", "" + s_d + s_fM);
+        test("8.075000000", "" + s_d + sf_i);
+        test("8.044", "" + s_d + f_b);
+        test("8.0-1705032704", "" + s_d + sf_lM);
+        test("8.0null", "" + s_d + f_oAN);
+        test("8.083.0", "" + s_d + f_d);
+        test("8.0I", "" + s_d + f_c);
+        test("8.094.0", "" + s_d + f_f);
+        test("8.012.0", "" + s_d + sf_d);
+        test("8.0-99.0", "" + s_d + f_dM);
+        test("8.017.0", "" + s_d + sf_f);
+        test("8.0-84.0", "" + s_d + sf_dM);
+        test("8.058000000", "" + s_d + f_i);
+        test("8.0-55000000", "" + s_d + f_iM);
+        test("8.01460392448", "" + s_d + f_l);
+        test("8.0C(70)", "" + s_d + f_o);
+        test("8.0\u04511", "" + s_d + sf_strU);
+        test("8.08000", "" + s_d + f_s);
+        test("8.018", "" + s_d + s_str);
+        test("8.0-1000000", "" + s_d + s_iM);
+        test("8.01000000", "" + s_d + sf_I);
+        test("8.0null", "" + s_d + f_oNtS);
+        test("8.0false", "" + s_d + f_bl);
+        test("8.0null", "" + s_d + sf_iAN);
+        test("8.0-2000000", "" + s_d + sf_iM);
+        test("8.0-820130816", "" + s_d + f_lM);
+        test("8.0null", "" + s_d + sf_oAN);
+        test("8.025000000", "" + s_d + s_I);
+        test("55.0-96.0", "" + s_f + s_dM);
+        test("55.0null", "" + s_f + s_oNtS);
+        test("55.0\u045176", "" + s_f + f_strU);
+        test("55.092", "" + s_f + sf_strU2);
+        test("55.051", "" + s_f + sf_strU1);
+        test("55.0null", "" + s_f + s_iAN);
+        test("55.0-54", "" + s_f + f_bM);
+        test("55.0-87.0", "" + s_f + f_fM);
+        test("55.0null", "" + s_f + s_oAN);
+        test("55.019", "" + s_f + f_str);
+        test("55.0-41", "" + s_f + sf_bM);
+        test("55.0null", "" + s_f + sf_IN);
+        test("55.0T", "" + s_f + s_c);
+        test("55.0-42.0", "" + s_f + sf_fM);
+        test("55.025", "" + s_f + s_b);
+        test("55.0null", "" + s_f + f_oN);
+        test("55.0-1410065408", "" + s_f + s_lM);
+        test("55.08.0", "" + s_f + s_d);
+        test("55.055.0", "" + s_f + s_f);
+        test("55.097000000", "" + s_f + s_i);
+        test("55.0-9900", "" + s_f + f_sM);
+        test("55.0935228928", "" + s_f + s_l);
+        test("55.0-8400", "" + s_f + sf_sM);
+        test("55.0C(82)", "" + s_f + s_o);
+        test("55.0null", "" + s_f + sf_oNtS);
+        test("55.0true", "" + s_f + s_bl);
+        test("55.03900", "" + s_f + s_s);
+        test("55.0null", "" + s_f + sf_oN);
+        test("55.094000000", "" + s_f + f_I);
+        test("55.0null", "" + s_f + f_IN);
+        test("55.0true", "" + s_f + sf_bl);
+        test("55.05500", "" + s_f + sf_s);
+        test("55.0-2900", "" + s_f + s_sM);
+        test("55.0-194313216", "" + s_f + sf_l);
+        test("55.012", "" + s_f + s_strU1);
+        test("55.0C(87)", "" + s_f + sf_o);
+        test("55.091", "" + s_f + s_strU2);
+        test("55.021", "" + s_f + f_strU1);
+        test("55.018", "" + s_f + f_strU2);
+        test("55.0null", "" + s_f + f_iAN);
+        test("55.0null", "" + s_f + s_oN);
+        test("55.0\u045180", "" + s_f + s_strU);
+        test("55.0C", "" + s_f + sf_c);
+        test("55.075", "" + s_f + sf_str);
+        test("55.0-43", "" + s_f + s_bM);
+        test("55.080", "" + s_f + sf_b);
+        test("55.0null", "" + s_f + s_IN);
+        test("55.0-52.0", "" + s_f + s_fM);
+        test("55.075000000", "" + s_f + sf_i);
+        test("55.044", "" + s_f + f_b);
+        test("55.0-1705032704", "" + s_f + sf_lM);
+        test("55.0null", "" + s_f + f_oAN);
+        test("55.083.0", "" + s_f + f_d);
+        test("55.0I", "" + s_f + f_c);
+        test("55.094.0", "" + s_f + f_f);
+        test("55.012.0", "" + s_f + sf_d);
+        test("55.0-99.0", "" + s_f + f_dM);
+        test("55.017.0", "" + s_f + sf_f);
+        test("55.0-84.0", "" + s_f + sf_dM);
+        test("55.058000000", "" + s_f + f_i);
+        test("55.0-55000000", "" + s_f + f_iM);
+        test("55.01460392448", "" + s_f + f_l);
+        test("55.0C(70)", "" + s_f + f_o);
+        test("55.0\u04511", "" + s_f + sf_strU);
+        test("55.08000", "" + s_f + f_s);
+        test("55.018", "" + s_f + s_str);
+        test("55.0-1000000", "" + s_f + s_iM);
+        test("55.01000000", "" + s_f + sf_I);
+        test("55.0null", "" + s_f + f_oNtS);
+        test("55.0false", "" + s_f + f_bl);
+        test("55.0null", "" + s_f + sf_iAN);
+        test("55.0-2000000", "" + s_f + sf_iM);
+        test("55.0-820130816", "" + s_f + f_lM);
+        test("55.0null", "" + s_f + sf_oAN);
+        test("55.025000000", "" + s_f + s_I);
+        test("97000000-96.0", "" + s_i + s_dM);
+        test("97000000null", "" + s_i + s_oNtS);
+        test("97000000\u045176", "" + s_i + f_strU);
+        test("9700000092", "" + s_i + sf_strU2);
+        test("9700000051", "" + s_i + sf_strU1);
+        test("97000000null", "" + s_i + s_iAN);
+        test("97000000-54", "" + s_i + f_bM);
+        test("97000000-87.0", "" + s_i + f_fM);
+        test("97000000null", "" + s_i + s_oAN);
+        test("9700000019", "" + s_i + f_str);
+        test("97000000-41", "" + s_i + sf_bM);
+        test("97000000null", "" + s_i + sf_IN);
+        test("97000000T", "" + s_i + s_c);
+        test("97000000-42.0", "" + s_i + sf_fM);
+        test("9700000025", "" + s_i + s_b);
+        test("97000000null", "" + s_i + f_oN);
+        test("97000000-1410065408", "" + s_i + s_lM);
+        test("970000008.0", "" + s_i + s_d);
+        test("9700000055.0", "" + s_i + s_f);
+        test("9700000097000000", "" + s_i + s_i);
+        test("97000000-9900", "" + s_i + f_sM);
+        test("97000000935228928", "" + s_i + s_l);
+        test("97000000-8400", "" + s_i + sf_sM);
+        test("97000000C(82)", "" + s_i + s_o);
+        test("97000000null", "" + s_i + sf_oNtS);
+        test("97000000true", "" + s_i + s_bl);
+        test("970000003900", "" + s_i + s_s);
+        test("97000000null", "" + s_i + sf_oN);
+        test("9700000094000000", "" + s_i + f_I);
+        test("97000000null", "" + s_i + f_IN);
+        test("97000000true", "" + s_i + sf_bl);
+        test("970000005500", "" + s_i + sf_s);
+        test("97000000-2900", "" + s_i + s_sM);
+        test("97000000-194313216", "" + s_i + sf_l);
+        test("9700000012", "" + s_i + s_strU1);
+        test("97000000C(87)", "" + s_i + sf_o);
+        test("9700000091", "" + s_i + s_strU2);
+        test("9700000021", "" + s_i + f_strU1);
+        test("9700000018", "" + s_i + f_strU2);
+        test("97000000null", "" + s_i + f_iAN);
+        test("97000000null", "" + s_i + s_oN);
+        test("97000000\u045180", "" + s_i + s_strU);
+        test("97000000C", "" + s_i + sf_c);
+        test("9700000075", "" + s_i + sf_str);
+        test("97000000-43", "" + s_i + s_bM);
+        test("9700000080", "" + s_i + sf_b);
+        test("97000000null", "" + s_i + s_IN);
+        test("97000000-52.0", "" + s_i + s_fM);
+        test("9700000075000000", "" + s_i + sf_i);
+        test("9700000044", "" + s_i + f_b);
+        test("97000000-1705032704", "" + s_i + sf_lM);
+        test("97000000null", "" + s_i + f_oAN);
+        test("9700000083.0", "" + s_i + f_d);
+        test("97000000I", "" + s_i + f_c);
+        test("9700000094.0", "" + s_i + f_f);
+        test("9700000012.0", "" + s_i + sf_d);
+        test("97000000-99.0", "" + s_i + f_dM);
+        test("9700000017.0", "" + s_i + sf_f);
+        test("97000000-84.0", "" + s_i + sf_dM);
+        test("9700000058000000", "" + s_i + f_i);
+        test("97000000-55000000", "" + s_i + f_iM);
+        test("970000001460392448", "" + s_i + f_l);
+        test("97000000C(70)", "" + s_i + f_o);
+        test("97000000\u04511", "" + s_i + sf_strU);
+        test("970000008000", "" + s_i + f_s);
+        test("9700000018", "" + s_i + s_str);
+        test("97000000-1000000", "" + s_i + s_iM);
+        test("970000001000000", "" + s_i + sf_I);
+        test("97000000null", "" + s_i + f_oNtS);
+        test("97000000false", "" + s_i + f_bl);
+        test("97000000null", "" + s_i + sf_iAN);
+        test("97000000-2000000", "" + s_i + sf_iM);
+        test("97000000-820130816", "" + s_i + f_lM);
+        test("97000000null", "" + s_i + sf_oAN);
+        test("9700000025000000", "" + s_i + s_I);
+        test("-9900-96.0", "" + f_sM + s_dM);
+        test("-9900null", "" + f_sM + s_oNtS);
+        test("-9900\u045176", "" + f_sM + f_strU);
+        test("-990092", "" + f_sM + sf_strU2);
+        test("-990051", "" + f_sM + sf_strU1);
+        test("-9900null", "" + f_sM + s_iAN);
+        test("-9900-54", "" + f_sM + f_bM);
+        test("-9900-87.0", "" + f_sM + f_fM);
+        test("-9900null", "" + f_sM + s_oAN);
+        test("-990019", "" + f_sM + f_str);
+        test("-9900-41", "" + f_sM + sf_bM);
+        test("-9900null", "" + f_sM + sf_IN);
+        test("-9900T", "" + f_sM + s_c);
+        test("-9900-42.0", "" + f_sM + sf_fM);
+        test("-990025", "" + f_sM + s_b);
+        test("-9900null", "" + f_sM + f_oN);
+        test("-9900-1410065408", "" + f_sM + s_lM);
+        test("-99008.0", "" + f_sM + s_d);
+        test("-990055.0", "" + f_sM + s_f);
+        test("-990097000000", "" + f_sM + s_i);
+        test("-9900-9900", "" + f_sM + f_sM);
+        test("-9900935228928", "" + f_sM + s_l);
+        test("-9900-8400", "" + f_sM + sf_sM);
+        test("-9900C(82)", "" + f_sM + s_o);
+        test("-9900null", "" + f_sM + sf_oNtS);
+        test("-9900true", "" + f_sM + s_bl);
+        test("-99003900", "" + f_sM + s_s);
+        test("-9900null", "" + f_sM + sf_oN);
+        test("-990094000000", "" + f_sM + f_I);
+        test("-9900null", "" + f_sM + f_IN);
+        test("-9900true", "" + f_sM + sf_bl);
+        test("-99005500", "" + f_sM + sf_s);
+        test("-9900-2900", "" + f_sM + s_sM);
+        test("-9900-194313216", "" + f_sM + sf_l);
+        test("-990012", "" + f_sM + s_strU1);
+        test("-9900C(87)", "" + f_sM + sf_o);
+        test("-990091", "" + f_sM + s_strU2);
+        test("-990021", "" + f_sM + f_strU1);
+        test("-990018", "" + f_sM + f_strU2);
+        test("-9900null", "" + f_sM + f_iAN);
+        test("-9900null", "" + f_sM + s_oN);
+        test("-9900\u045180", "" + f_sM + s_strU);
+        test("-9900C", "" + f_sM + sf_c);
+        test("-990075", "" + f_sM + sf_str);
+        test("-9900-43", "" + f_sM + s_bM);
+        test("-990080", "" + f_sM + sf_b);
+        test("-9900null", "" + f_sM + s_IN);
+        test("-9900-52.0", "" + f_sM + s_fM);
+        test("-990075000000", "" + f_sM + sf_i);
+        test("-990044", "" + f_sM + f_b);
+        test("-9900-1705032704", "" + f_sM + sf_lM);
+        test("-9900null", "" + f_sM + f_oAN);
+        test("-990083.0", "" + f_sM + f_d);
+        test("-9900I", "" + f_sM + f_c);
+        test("-990094.0", "" + f_sM + f_f);
+        test("-990012.0", "" + f_sM + sf_d);
+        test("-9900-99.0", "" + f_sM + f_dM);
+        test("-990017.0", "" + f_sM + sf_f);
+        test("-9900-84.0", "" + f_sM + sf_dM);
+        test("-990058000000", "" + f_sM + f_i);
+        test("-9900-55000000", "" + f_sM + f_iM);
+        test("-99001460392448", "" + f_sM + f_l);
+        test("-9900C(70)", "" + f_sM + f_o);
+        test("-9900\u04511", "" + f_sM + sf_strU);
+        test("-99008000", "" + f_sM + f_s);
+        test("-990018", "" + f_sM + s_str);
+        test("-9900-1000000", "" + f_sM + s_iM);
+        test("-99001000000", "" + f_sM + sf_I);
+        test("-9900null", "" + f_sM + f_oNtS);
+        test("-9900false", "" + f_sM + f_bl);
+        test("-9900null", "" + f_sM + sf_iAN);
+        test("-9900-2000000", "" + f_sM + sf_iM);
+        test("-9900-820130816", "" + f_sM + f_lM);
+        test("-9900null", "" + f_sM + sf_oAN);
+        test("-990025000000", "" + f_sM + s_I);
+        test("935228928-96.0", "" + s_l + s_dM);
+        test("935228928null", "" + s_l + s_oNtS);
+        test("935228928\u045176", "" + s_l + f_strU);
+        test("93522892892", "" + s_l + sf_strU2);
+        test("93522892851", "" + s_l + sf_strU1);
+        test("935228928null", "" + s_l + s_iAN);
+        test("935228928-54", "" + s_l + f_bM);
+        test("935228928-87.0", "" + s_l + f_fM);
+        test("935228928null", "" + s_l + s_oAN);
+        test("93522892819", "" + s_l + f_str);
+        test("935228928-41", "" + s_l + sf_bM);
+        test("935228928null", "" + s_l + sf_IN);
+        test("935228928T", "" + s_l + s_c);
+        test("935228928-42.0", "" + s_l + sf_fM);
+        test("93522892825", "" + s_l + s_b);
+        test("935228928null", "" + s_l + f_oN);
+        test("935228928-1410065408", "" + s_l + s_lM);
+        test("9352289288.0", "" + s_l + s_d);
+        test("93522892855.0", "" + s_l + s_f);
+        test("93522892897000000", "" + s_l + s_i);
+        test("935228928-9900", "" + s_l + f_sM);
+        test("935228928935228928", "" + s_l + s_l);
+        test("935228928-8400", "" + s_l + sf_sM);
+        test("935228928C(82)", "" + s_l + s_o);
+        test("935228928null", "" + s_l + sf_oNtS);
+        test("935228928true", "" + s_l + s_bl);
+        test("9352289283900", "" + s_l + s_s);
+        test("935228928null", "" + s_l + sf_oN);
+        test("93522892894000000", "" + s_l + f_I);
+        test("935228928null", "" + s_l + f_IN);
+        test("935228928true", "" + s_l + sf_bl);
+        test("9352289285500", "" + s_l + sf_s);
+        test("935228928-2900", "" + s_l + s_sM);
+        test("935228928-194313216", "" + s_l + sf_l);
+        test("93522892812", "" + s_l + s_strU1);
+        test("935228928C(87)", "" + s_l + sf_o);
+        test("93522892891", "" + s_l + s_strU2);
+        test("93522892821", "" + s_l + f_strU1);
+        test("93522892818", "" + s_l + f_strU2);
+        test("935228928null", "" + s_l + f_iAN);
+        test("935228928null", "" + s_l + s_oN);
+        test("935228928\u045180", "" + s_l + s_strU);
+        test("935228928C", "" + s_l + sf_c);
+        test("93522892875", "" + s_l + sf_str);
+        test("935228928-43", "" + s_l + s_bM);
+        test("93522892880", "" + s_l + sf_b);
+        test("935228928null", "" + s_l + s_IN);
+        test("935228928-52.0", "" + s_l + s_fM);
+        test("93522892875000000", "" + s_l + sf_i);
+        test("93522892844", "" + s_l + f_b);
+        test("935228928-1705032704", "" + s_l + sf_lM);
+        test("935228928null", "" + s_l + f_oAN);
+        test("93522892883.0", "" + s_l + f_d);
+        test("935228928I", "" + s_l + f_c);
+        test("93522892894.0", "" + s_l + f_f);
+        test("93522892812.0", "" + s_l + sf_d);
+        test("935228928-99.0", "" + s_l + f_dM);
+        test("93522892817.0", "" + s_l + sf_f);
+        test("935228928-84.0", "" + s_l + sf_dM);
+        test("93522892858000000", "" + s_l + f_i);
+        test("935228928-55000000", "" + s_l + f_iM);
+        test("9352289281460392448", "" + s_l + f_l);
+        test("935228928C(70)", "" + s_l + f_o);
+        test("935228928\u04511", "" + s_l + sf_strU);
+        test("9352289288000", "" + s_l + f_s);
+        test("93522892818", "" + s_l + s_str);
+        test("935228928-1000000", "" + s_l + s_iM);
+        test("9352289281000000", "" + s_l + sf_I);
+        test("935228928null", "" + s_l + f_oNtS);
+        test("935228928false", "" + s_l + f_bl);
+        test("935228928null", "" + s_l + sf_iAN);
+        test("935228928-2000000", "" + s_l + sf_iM);
+        test("935228928-820130816", "" + s_l + f_lM);
+        test("935228928null", "" + s_l + sf_oAN);
+        test("93522892825000000", "" + s_l + s_I);
+        test("-8400-96.0", "" + sf_sM + s_dM);
+        test("-8400null", "" + sf_sM + s_oNtS);
+        test("-8400\u045176", "" + sf_sM + f_strU);
+        test("-840092", "" + sf_sM + sf_strU2);
+        test("-840051", "" + sf_sM + sf_strU1);
+        test("-8400null", "" + sf_sM + s_iAN);
+        test("-8400-54", "" + sf_sM + f_bM);
+        test("-8400-87.0", "" + sf_sM + f_fM);
+        test("-8400null", "" + sf_sM + s_oAN);
+        test("-840019", "" + sf_sM + f_str);
+        test("-8400-41", "" + sf_sM + sf_bM);
+        test("-8400null", "" + sf_sM + sf_IN);
+        test("-8400T", "" + sf_sM + s_c);
+        test("-8400-42.0", "" + sf_sM + sf_fM);
+        test("-840025", "" + sf_sM + s_b);
+        test("-8400null", "" + sf_sM + f_oN);
+        test("-8400-1410065408", "" + sf_sM + s_lM);
+        test("-84008.0", "" + sf_sM + s_d);
+        test("-840055.0", "" + sf_sM + s_f);
+        test("-840097000000", "" + sf_sM + s_i);
+        test("-8400-9900", "" + sf_sM + f_sM);
+        test("-8400935228928", "" + sf_sM + s_l);
+        test("-8400-8400", "" + sf_sM + sf_sM);
+        test("-8400C(82)", "" + sf_sM + s_o);
+        test("-8400null", "" + sf_sM + sf_oNtS);
+        test("-8400true", "" + sf_sM + s_bl);
+        test("-84003900", "" + sf_sM + s_s);
+        test("-8400null", "" + sf_sM + sf_oN);
+        test("-840094000000", "" + sf_sM + f_I);
+        test("-8400null", "" + sf_sM + f_IN);
+        test("-8400true", "" + sf_sM + sf_bl);
+        test("-84005500", "" + sf_sM + sf_s);
+        test("-8400-2900", "" + sf_sM + s_sM);
+        test("-8400-194313216", "" + sf_sM + sf_l);
+        test("-840012", "" + sf_sM + s_strU1);
+        test("-8400C(87)", "" + sf_sM + sf_o);
+        test("-840091", "" + sf_sM + s_strU2);
+        test("-840021", "" + sf_sM + f_strU1);
+        test("-840018", "" + sf_sM + f_strU2);
+        test("-8400null", "" + sf_sM + f_iAN);
+        test("-8400null", "" + sf_sM + s_oN);
+        test("-8400\u045180", "" + sf_sM + s_strU);
+        test("-8400C", "" + sf_sM + sf_c);
+        test("-840075", "" + sf_sM + sf_str);
+        test("-8400-43", "" + sf_sM + s_bM);
+        test("-840080", "" + sf_sM + sf_b);
+        test("-8400null", "" + sf_sM + s_IN);
+        test("-8400-52.0", "" + sf_sM + s_fM);
+        test("-840075000000", "" + sf_sM + sf_i);
+        test("-840044", "" + sf_sM + f_b);
+        test("-8400-1705032704", "" + sf_sM + sf_lM);
+        test("-8400null", "" + sf_sM + f_oAN);
+        test("-840083.0", "" + sf_sM + f_d);
+        test("-8400I", "" + sf_sM + f_c);
+        test("-840094.0", "" + sf_sM + f_f);
+        test("-840012.0", "" + sf_sM + sf_d);
+        test("-8400-99.0", "" + sf_sM + f_dM);
+        test("-840017.0", "" + sf_sM + sf_f);
+        test("-8400-84.0", "" + sf_sM + sf_dM);
+        test("-840058000000", "" + sf_sM + f_i);
+        test("-8400-55000000", "" + sf_sM + f_iM);
+        test("-84001460392448", "" + sf_sM + f_l);
+        test("-8400C(70)", "" + sf_sM + f_o);
+        test("-8400\u04511", "" + sf_sM + sf_strU);
+        test("-84008000", "" + sf_sM + f_s);
+        test("-840018", "" + sf_sM + s_str);
+        test("-8400-1000000", "" + sf_sM + s_iM);
+        test("-84001000000", "" + sf_sM + sf_I);
+        test("-8400null", "" + sf_sM + f_oNtS);
+        test("-8400false", "" + sf_sM + f_bl);
+        test("-8400null", "" + sf_sM + sf_iAN);
+        test("-8400-2000000", "" + sf_sM + sf_iM);
+        test("-8400-820130816", "" + sf_sM + f_lM);
+        test("-8400null", "" + sf_sM + sf_oAN);
+        test("-840025000000", "" + sf_sM + s_I);
+        test("C(82)-96.0", "" + s_o + s_dM);
+        test("C(82)null", "" + s_o + s_oNtS);
+        test("C(82)\u045176", "" + s_o + f_strU);
+        test("C(82)92", "" + s_o + sf_strU2);
+        test("C(82)51", "" + s_o + sf_strU1);
+        test("C(82)null", "" + s_o + s_iAN);
+        test("C(82)-54", "" + s_o + f_bM);
+        test("C(82)-87.0", "" + s_o + f_fM);
+        test("C(82)null", "" + s_o + s_oAN);
+        test("C(82)19", "" + s_o + f_str);
+        test("C(82)-41", "" + s_o + sf_bM);
+        test("C(82)null", "" + s_o + sf_IN);
+        test("C(82)T", "" + s_o + s_c);
+        test("C(82)-42.0", "" + s_o + sf_fM);
+        test("C(82)25", "" + s_o + s_b);
+        test("C(82)null", "" + s_o + f_oN);
+        test("C(82)-1410065408", "" + s_o + s_lM);
+        test("C(82)8.0", "" + s_o + s_d);
+        test("C(82)55.0", "" + s_o + s_f);
+        test("C(82)97000000", "" + s_o + s_i);
+        test("C(82)-9900", "" + s_o + f_sM);
+        test("C(82)935228928", "" + s_o + s_l);
+        test("C(82)-8400", "" + s_o + sf_sM);
+        test("C(82)C(82)", "" + s_o + s_o);
+        test("C(82)null", "" + s_o + sf_oNtS);
+        test("C(82)true", "" + s_o + s_bl);
+        test("C(82)3900", "" + s_o + s_s);
+        test("C(82)null", "" + s_o + sf_oN);
+        test("C(82)94000000", "" + s_o + f_I);
+        test("C(82)null", "" + s_o + f_IN);
+        test("C(82)true", "" + s_o + sf_bl);
+        test("C(82)5500", "" + s_o + sf_s);
+        test("C(82)-2900", "" + s_o + s_sM);
+        test("C(82)-194313216", "" + s_o + sf_l);
+        test("C(82)12", "" + s_o + s_strU1);
+        test("C(82)C(87)", "" + s_o + sf_o);
+        test("C(82)91", "" + s_o + s_strU2);
+        test("C(82)21", "" + s_o + f_strU1);
+        test("C(82)18", "" + s_o + f_strU2);
+        test("C(82)null", "" + s_o + f_iAN);
+        test("C(82)null", "" + s_o + s_oN);
+        test("C(82)\u045180", "" + s_o + s_strU);
+        test("C(82)C", "" + s_o + sf_c);
+        test("C(82)75", "" + s_o + sf_str);
+        test("C(82)-43", "" + s_o + s_bM);
+        test("C(82)80", "" + s_o + sf_b);
+        test("C(82)null", "" + s_o + s_IN);
+        test("C(82)-52.0", "" + s_o + s_fM);
+        test("C(82)75000000", "" + s_o + sf_i);
+        test("C(82)44", "" + s_o + f_b);
+        test("C(82)-1705032704", "" + s_o + sf_lM);
+        test("C(82)null", "" + s_o + f_oAN);
+        test("C(82)83.0", "" + s_o + f_d);
+        test("C(82)I", "" + s_o + f_c);
+        test("C(82)94.0", "" + s_o + f_f);
+        test("C(82)12.0", "" + s_o + sf_d);
+        test("C(82)-99.0", "" + s_o + f_dM);
+        test("C(82)17.0", "" + s_o + sf_f);
+        test("C(82)-84.0", "" + s_o + sf_dM);
+        test("C(82)58000000", "" + s_o + f_i);
+        test("C(82)-55000000", "" + s_o + f_iM);
+        test("C(82)1460392448", "" + s_o + f_l);
+        test("C(82)C(70)", "" + s_o + f_o);
+        test("C(82)\u04511", "" + s_o + sf_strU);
+        test("C(82)8000", "" + s_o + f_s);
+        test("C(82)18", "" + s_o + s_str);
+        test("C(82)-1000000", "" + s_o + s_iM);
+        test("C(82)1000000", "" + s_o + sf_I);
+        test("C(82)null", "" + s_o + f_oNtS);
+        test("C(82)false", "" + s_o + f_bl);
+        test("C(82)null", "" + s_o + sf_iAN);
+        test("C(82)-2000000", "" + s_o + sf_iM);
+        test("C(82)-820130816", "" + s_o + f_lM);
+        test("C(82)null", "" + s_o + sf_oAN);
+        test("C(82)25000000", "" + s_o + s_I);
+        test("null-96.0", "" + sf_oNtS + s_dM);
+        test("nullnull", "" + sf_oNtS + s_oNtS);
+        test("null\u045176", "" + sf_oNtS + f_strU);
+        test("null92", "" + sf_oNtS + sf_strU2);
+        test("null51", "" + sf_oNtS + sf_strU1);
+        test("nullnull", "" + sf_oNtS + s_iAN);
+        test("null-54", "" + sf_oNtS + f_bM);
+        test("null-87.0", "" + sf_oNtS + f_fM);
+        test("nullnull", "" + sf_oNtS + s_oAN);
+        test("null19", "" + sf_oNtS + f_str);
+        test("null-41", "" + sf_oNtS + sf_bM);
+        test("nullnull", "" + sf_oNtS + sf_IN);
+        test("nullT", "" + sf_oNtS + s_c);
+        test("null-42.0", "" + sf_oNtS + sf_fM);
+        test("null25", "" + sf_oNtS + s_b);
+        test("nullnull", "" + sf_oNtS + f_oN);
+        test("null-1410065408", "" + sf_oNtS + s_lM);
+        test("null8.0", "" + sf_oNtS + s_d);
+        test("null55.0", "" + sf_oNtS + s_f);
+        test("null97000000", "" + sf_oNtS + s_i);
+        test("null-9900", "" + sf_oNtS + f_sM);
+        test("null935228928", "" + sf_oNtS + s_l);
+        test("null-8400", "" + sf_oNtS + sf_sM);
+        test("nullC(82)", "" + sf_oNtS + s_o);
+        test("nullnull", "" + sf_oNtS + sf_oNtS);
+        test("nulltrue", "" + sf_oNtS + s_bl);
+        test("null3900", "" + sf_oNtS + s_s);
+        test("nullnull", "" + sf_oNtS + sf_oN);
+        test("null94000000", "" + sf_oNtS + f_I);
+        test("nullnull", "" + sf_oNtS + f_IN);
+        test("nulltrue", "" + sf_oNtS + sf_bl);
+        test("null5500", "" + sf_oNtS + sf_s);
+        test("null-2900", "" + sf_oNtS + s_sM);
+        test("null-194313216", "" + sf_oNtS + sf_l);
+        test("null12", "" + sf_oNtS + s_strU1);
+        test("nullC(87)", "" + sf_oNtS + sf_o);
+        test("null91", "" + sf_oNtS + s_strU2);
+        test("null21", "" + sf_oNtS + f_strU1);
+        test("null18", "" + sf_oNtS + f_strU2);
+        test("nullnull", "" + sf_oNtS + f_iAN);
+        test("nullnull", "" + sf_oNtS + s_oN);
+        test("null\u045180", "" + sf_oNtS + s_strU);
+        test("nullC", "" + sf_oNtS + sf_c);
+        test("null75", "" + sf_oNtS + sf_str);
+        test("null-43", "" + sf_oNtS + s_bM);
+        test("null80", "" + sf_oNtS + sf_b);
+        test("nullnull", "" + sf_oNtS + s_IN);
+        test("null-52.0", "" + sf_oNtS + s_fM);
+        test("null75000000", "" + sf_oNtS + sf_i);
+        test("null44", "" + sf_oNtS + f_b);
+        test("null-1705032704", "" + sf_oNtS + sf_lM);
+        test("nullnull", "" + sf_oNtS + f_oAN);
+        test("null83.0", "" + sf_oNtS + f_d);
+        test("nullI", "" + sf_oNtS + f_c);
+        test("null94.0", "" + sf_oNtS + f_f);
+        test("null12.0", "" + sf_oNtS + sf_d);
+        test("null-99.0", "" + sf_oNtS + f_dM);
+        test("null17.0", "" + sf_oNtS + sf_f);
+        test("null-84.0", "" + sf_oNtS + sf_dM);
+        test("null58000000", "" + sf_oNtS + f_i);
+        test("null-55000000", "" + sf_oNtS + f_iM);
+        test("null1460392448", "" + sf_oNtS + f_l);
+        test("nullC(70)", "" + sf_oNtS + f_o);
+        test("null\u04511", "" + sf_oNtS + sf_strU);
+        test("null8000", "" + sf_oNtS + f_s);
+        test("null18", "" + sf_oNtS + s_str);
+        test("null-1000000", "" + sf_oNtS + s_iM);
+        test("null1000000", "" + sf_oNtS + sf_I);
+        test("nullnull", "" + sf_oNtS + f_oNtS);
+        test("nullfalse", "" + sf_oNtS + f_bl);
+        test("nullnull", "" + sf_oNtS + sf_iAN);
+        test("null-2000000", "" + sf_oNtS + sf_iM);
+        test("null-820130816", "" + sf_oNtS + f_lM);
+        test("nullnull", "" + sf_oNtS + sf_oAN);
+        test("null25000000", "" + sf_oNtS + s_I);
+        test("true-96.0", "" + s_bl + s_dM);
+        test("truenull", "" + s_bl + s_oNtS);
+        test("true\u045176", "" + s_bl + f_strU);
+        test("true92", "" + s_bl + sf_strU2);
+        test("true51", "" + s_bl + sf_strU1);
+        test("truenull", "" + s_bl + s_iAN);
+        test("true-54", "" + s_bl + f_bM);
+        test("true-87.0", "" + s_bl + f_fM);
+        test("truenull", "" + s_bl + s_oAN);
+        test("true19", "" + s_bl + f_str);
+        test("true-41", "" + s_bl + sf_bM);
+        test("truenull", "" + s_bl + sf_IN);
+        test("trueT", "" + s_bl + s_c);
+        test("true-42.0", "" + s_bl + sf_fM);
+        test("true25", "" + s_bl + s_b);
+        test("truenull", "" + s_bl + f_oN);
+        test("true-1410065408", "" + s_bl + s_lM);
+        test("true8.0", "" + s_bl + s_d);
+        test("true55.0", "" + s_bl + s_f);
+        test("true97000000", "" + s_bl + s_i);
+        test("true-9900", "" + s_bl + f_sM);
+        test("true935228928", "" + s_bl + s_l);
+        test("true-8400", "" + s_bl + sf_sM);
+        test("trueC(82)", "" + s_bl + s_o);
+        test("truenull", "" + s_bl + sf_oNtS);
+        test("truetrue", "" + s_bl + s_bl);
+        test("true3900", "" + s_bl + s_s);
+        test("truenull", "" + s_bl + sf_oN);
+        test("true94000000", "" + s_bl + f_I);
+        test("truenull", "" + s_bl + f_IN);
+        test("truetrue", "" + s_bl + sf_bl);
+        test("true5500", "" + s_bl + sf_s);
+        test("true-2900", "" + s_bl + s_sM);
+        test("true-194313216", "" + s_bl + sf_l);
+        test("true12", "" + s_bl + s_strU1);
+        test("trueC(87)", "" + s_bl + sf_o);
+        test("true91", "" + s_bl + s_strU2);
+        test("true21", "" + s_bl + f_strU1);
+        test("true18", "" + s_bl + f_strU2);
+        test("truenull", "" + s_bl + f_iAN);
+        test("truenull", "" + s_bl + s_oN);
+        test("true\u045180", "" + s_bl + s_strU);
+        test("trueC", "" + s_bl + sf_c);
+        test("true75", "" + s_bl + sf_str);
+        test("true-43", "" + s_bl + s_bM);
+        test("true80", "" + s_bl + sf_b);
+        test("truenull", "" + s_bl + s_IN);
+        test("true-52.0", "" + s_bl + s_fM);
+        test("true75000000", "" + s_bl + sf_i);
+        test("true44", "" + s_bl + f_b);
+    }
+
+    public void run2() {
+        test("true-1705032704", "" + s_bl + sf_lM);
+        test("truenull", "" + s_bl + f_oAN);
+        test("true83.0", "" + s_bl + f_d);
+        test("trueI", "" + s_bl + f_c);
+        test("true94.0", "" + s_bl + f_f);
+        test("true12.0", "" + s_bl + sf_d);
+        test("true-99.0", "" + s_bl + f_dM);
+        test("true17.0", "" + s_bl + sf_f);
+        test("true-84.0", "" + s_bl + sf_dM);
+        test("true58000000", "" + s_bl + f_i);
+        test("true-55000000", "" + s_bl + f_iM);
+        test("true1460392448", "" + s_bl + f_l);
+        test("trueC(70)", "" + s_bl + f_o);
+        test("true\u04511", "" + s_bl + sf_strU);
+        test("true8000", "" + s_bl + f_s);
+        test("true18", "" + s_bl + s_str);
+        test("true-1000000", "" + s_bl + s_iM);
+        test("true1000000", "" + s_bl + sf_I);
+        test("truenull", "" + s_bl + f_oNtS);
+        test("truefalse", "" + s_bl + f_bl);
+        test("truenull", "" + s_bl + sf_iAN);
+        test("true-2000000", "" + s_bl + sf_iM);
+        test("true-820130816", "" + s_bl + f_lM);
+        test("truenull", "" + s_bl + sf_oAN);
+        test("true25000000", "" + s_bl + s_I);
+        test("3900-96.0", "" + s_s + s_dM);
+        test("3900null", "" + s_s + s_oNtS);
+        test("3900\u045176", "" + s_s + f_strU);
+        test("390092", "" + s_s + sf_strU2);
+        test("390051", "" + s_s + sf_strU1);
+        test("3900null", "" + s_s + s_iAN);
+        test("3900-54", "" + s_s + f_bM);
+        test("3900-87.0", "" + s_s + f_fM);
+        test("3900null", "" + s_s + s_oAN);
+        test("390019", "" + s_s + f_str);
+        test("3900-41", "" + s_s + sf_bM);
+        test("3900null", "" + s_s + sf_IN);
+        test("3900T", "" + s_s + s_c);
+        test("3900-42.0", "" + s_s + sf_fM);
+        test("390025", "" + s_s + s_b);
+        test("3900null", "" + s_s + f_oN);
+        test("3900-1410065408", "" + s_s + s_lM);
+        test("39008.0", "" + s_s + s_d);
+        test("390055.0", "" + s_s + s_f);
+        test("390097000000", "" + s_s + s_i);
+        test("3900-9900", "" + s_s + f_sM);
+        test("3900935228928", "" + s_s + s_l);
+        test("3900-8400", "" + s_s + sf_sM);
+        test("3900C(82)", "" + s_s + s_o);
+        test("3900null", "" + s_s + sf_oNtS);
+        test("3900true", "" + s_s + s_bl);
+        test("39003900", "" + s_s + s_s);
+        test("3900null", "" + s_s + sf_oN);
+        test("390094000000", "" + s_s + f_I);
+        test("3900null", "" + s_s + f_IN);
+        test("3900true", "" + s_s + sf_bl);
+        test("39005500", "" + s_s + sf_s);
+        test("3900-2900", "" + s_s + s_sM);
+        test("3900-194313216", "" + s_s + sf_l);
+        test("390012", "" + s_s + s_strU1);
+        test("3900C(87)", "" + s_s + sf_o);
+        test("390091", "" + s_s + s_strU2);
+        test("390021", "" + s_s + f_strU1);
+        test("390018", "" + s_s + f_strU2);
+        test("3900null", "" + s_s + f_iAN);
+        test("3900null", "" + s_s + s_oN);
+        test("3900\u045180", "" + s_s + s_strU);
+        test("3900C", "" + s_s + sf_c);
+        test("390075", "" + s_s + sf_str);
+        test("3900-43", "" + s_s + s_bM);
+        test("390080", "" + s_s + sf_b);
+        test("3900null", "" + s_s + s_IN);
+        test("3900-52.0", "" + s_s + s_fM);
+        test("390075000000", "" + s_s + sf_i);
+        test("390044", "" + s_s + f_b);
+        test("3900-1705032704", "" + s_s + sf_lM);
+        test("3900null", "" + s_s + f_oAN);
+        test("390083.0", "" + s_s + f_d);
+        test("3900I", "" + s_s + f_c);
+        test("390094.0", "" + s_s + f_f);
+        test("390012.0", "" + s_s + sf_d);
+        test("3900-99.0", "" + s_s + f_dM);
+        test("390017.0", "" + s_s + sf_f);
+        test("3900-84.0", "" + s_s + sf_dM);
+        test("390058000000", "" + s_s + f_i);
+        test("3900-55000000", "" + s_s + f_iM);
+        test("39001460392448", "" + s_s + f_l);
+        test("3900C(70)", "" + s_s + f_o);
+        test("3900\u04511", "" + s_s + sf_strU);
+        test("39008000", "" + s_s + f_s);
+        test("390018", "" + s_s + s_str);
+        test("3900-1000000", "" + s_s + s_iM);
+        test("39001000000", "" + s_s + sf_I);
+        test("3900null", "" + s_s + f_oNtS);
+        test("3900false", "" + s_s + f_bl);
+        test("3900null", "" + s_s + sf_iAN);
+        test("3900-2000000", "" + s_s + sf_iM);
+        test("3900-820130816", "" + s_s + f_lM);
+        test("3900null", "" + s_s + sf_oAN);
+        test("390025000000", "" + s_s + s_I);
+        test("null-96.0", "" + sf_oN + s_dM);
+        test("nullnull", "" + sf_oN + s_oNtS);
+        test("null\u045176", "" + sf_oN + f_strU);
+        test("null92", "" + sf_oN + sf_strU2);
+        test("null51", "" + sf_oN + sf_strU1);
+        test("nullnull", "" + sf_oN + s_iAN);
+        test("null-54", "" + sf_oN + f_bM);
+        test("null-87.0", "" + sf_oN + f_fM);
+        test("nullnull", "" + sf_oN + s_oAN);
+        test("null19", "" + sf_oN + f_str);
+        test("null-41", "" + sf_oN + sf_bM);
+        test("nullnull", "" + sf_oN + sf_IN);
+        test("nullT", "" + sf_oN + s_c);
+        test("null-42.0", "" + sf_oN + sf_fM);
+        test("null25", "" + sf_oN + s_b);
+        test("nullnull", "" + sf_oN + f_oN);
+        test("null-1410065408", "" + sf_oN + s_lM);
+        test("null8.0", "" + sf_oN + s_d);
+        test("null55.0", "" + sf_oN + s_f);
+        test("null97000000", "" + sf_oN + s_i);
+        test("null-9900", "" + sf_oN + f_sM);
+        test("null935228928", "" + sf_oN + s_l);
+        test("null-8400", "" + sf_oN + sf_sM);
+        test("nullC(82)", "" + sf_oN + s_o);
+        test("nullnull", "" + sf_oN + sf_oNtS);
+        test("nulltrue", "" + sf_oN + s_bl);
+        test("null3900", "" + sf_oN + s_s);
+        test("nullnull", "" + sf_oN + sf_oN);
+        test("null94000000", "" + sf_oN + f_I);
+        test("nullnull", "" + sf_oN + f_IN);
+        test("nulltrue", "" + sf_oN + sf_bl);
+        test("null5500", "" + sf_oN + sf_s);
+        test("null-2900", "" + sf_oN + s_sM);
+        test("null-194313216", "" + sf_oN + sf_l);
+        test("null12", "" + sf_oN + s_strU1);
+        test("nullC(87)", "" + sf_oN + sf_o);
+        test("null91", "" + sf_oN + s_strU2);
+        test("null21", "" + sf_oN + f_strU1);
+        test("null18", "" + sf_oN + f_strU2);
+        test("nullnull", "" + sf_oN + f_iAN);
+        test("nullnull", "" + sf_oN + s_oN);
+        test("null\u045180", "" + sf_oN + s_strU);
+        test("nullC", "" + sf_oN + sf_c);
+        test("null75", "" + sf_oN + sf_str);
+        test("null-43", "" + sf_oN + s_bM);
+        test("null80", "" + sf_oN + sf_b);
+        test("nullnull", "" + sf_oN + s_IN);
+        test("null-52.0", "" + sf_oN + s_fM);
+        test("null75000000", "" + sf_oN + sf_i);
+        test("null44", "" + sf_oN + f_b);
+        test("null-1705032704", "" + sf_oN + sf_lM);
+        test("nullnull", "" + sf_oN + f_oAN);
+        test("null83.0", "" + sf_oN + f_d);
+        test("nullI", "" + sf_oN + f_c);
+        test("null94.0", "" + sf_oN + f_f);
+        test("null12.0", "" + sf_oN + sf_d);
+        test("null-99.0", "" + sf_oN + f_dM);
+        test("null17.0", "" + sf_oN + sf_f);
+        test("null-84.0", "" + sf_oN + sf_dM);
+        test("null58000000", "" + sf_oN + f_i);
+        test("null-55000000", "" + sf_oN + f_iM);
+        test("null1460392448", "" + sf_oN + f_l);
+        test("nullC(70)", "" + sf_oN + f_o);
+        test("null\u04511", "" + sf_oN + sf_strU);
+        test("null8000", "" + sf_oN + f_s);
+        test("null18", "" + sf_oN + s_str);
+        test("null-1000000", "" + sf_oN + s_iM);
+        test("null1000000", "" + sf_oN + sf_I);
+        test("nullnull", "" + sf_oN + f_oNtS);
+        test("nullfalse", "" + sf_oN + f_bl);
+        test("nullnull", "" + sf_oN + sf_iAN);
+        test("null-2000000", "" + sf_oN + sf_iM);
+        test("null-820130816", "" + sf_oN + f_lM);
+        test("nullnull", "" + sf_oN + sf_oAN);
+        test("null25000000", "" + sf_oN + s_I);
+        test("94000000-96.0", "" + f_I + s_dM);
+        test("94000000null", "" + f_I + s_oNtS);
+        test("94000000\u045176", "" + f_I + f_strU);
+        test("9400000092", "" + f_I + sf_strU2);
+        test("9400000051", "" + f_I + sf_strU1);
+        test("94000000null", "" + f_I + s_iAN);
+        test("94000000-54", "" + f_I + f_bM);
+        test("94000000-87.0", "" + f_I + f_fM);
+        test("94000000null", "" + f_I + s_oAN);
+        test("9400000019", "" + f_I + f_str);
+        test("94000000-41", "" + f_I + sf_bM);
+        test("94000000null", "" + f_I + sf_IN);
+        test("94000000T", "" + f_I + s_c);
+        test("94000000-42.0", "" + f_I + sf_fM);
+        test("9400000025", "" + f_I + s_b);
+        test("94000000null", "" + f_I + f_oN);
+        test("94000000-1410065408", "" + f_I + s_lM);
+        test("940000008.0", "" + f_I + s_d);
+        test("9400000055.0", "" + f_I + s_f);
+        test("9400000097000000", "" + f_I + s_i);
+        test("94000000-9900", "" + f_I + f_sM);
+        test("94000000935228928", "" + f_I + s_l);
+        test("94000000-8400", "" + f_I + sf_sM);
+        test("94000000C(82)", "" + f_I + s_o);
+        test("94000000null", "" + f_I + sf_oNtS);
+        test("94000000true", "" + f_I + s_bl);
+        test("940000003900", "" + f_I + s_s);
+        test("94000000null", "" + f_I + sf_oN);
+        test("9400000094000000", "" + f_I + f_I);
+        test("94000000null", "" + f_I + f_IN);
+        test("94000000true", "" + f_I + sf_bl);
+        test("940000005500", "" + f_I + sf_s);
+        test("94000000-2900", "" + f_I + s_sM);
+        test("94000000-194313216", "" + f_I + sf_l);
+        test("9400000012", "" + f_I + s_strU1);
+        test("94000000C(87)", "" + f_I + sf_o);
+        test("9400000091", "" + f_I + s_strU2);
+        test("9400000021", "" + f_I + f_strU1);
+        test("9400000018", "" + f_I + f_strU2);
+        test("94000000null", "" + f_I + f_iAN);
+        test("94000000null", "" + f_I + s_oN);
+        test("94000000\u045180", "" + f_I + s_strU);
+        test("94000000C", "" + f_I + sf_c);
+        test("9400000075", "" + f_I + sf_str);
+        test("94000000-43", "" + f_I + s_bM);
+        test("9400000080", "" + f_I + sf_b);
+        test("94000000null", "" + f_I + s_IN);
+        test("94000000-52.0", "" + f_I + s_fM);
+        test("9400000075000000", "" + f_I + sf_i);
+        test("9400000044", "" + f_I + f_b);
+        test("94000000-1705032704", "" + f_I + sf_lM);
+        test("94000000null", "" + f_I + f_oAN);
+        test("9400000083.0", "" + f_I + f_d);
+        test("94000000I", "" + f_I + f_c);
+        test("9400000094.0", "" + f_I + f_f);
+        test("9400000012.0", "" + f_I + sf_d);
+        test("94000000-99.0", "" + f_I + f_dM);
+        test("9400000017.0", "" + f_I + sf_f);
+        test("94000000-84.0", "" + f_I + sf_dM);
+        test("9400000058000000", "" + f_I + f_i);
+        test("94000000-55000000", "" + f_I + f_iM);
+        test("940000001460392448", "" + f_I + f_l);
+        test("94000000C(70)", "" + f_I + f_o);
+        test("94000000\u04511", "" + f_I + sf_strU);
+        test("940000008000", "" + f_I + f_s);
+        test("9400000018", "" + f_I + s_str);
+        test("94000000-1000000", "" + f_I + s_iM);
+        test("940000001000000", "" + f_I + sf_I);
+        test("94000000null", "" + f_I + f_oNtS);
+        test("94000000false", "" + f_I + f_bl);
+        test("94000000null", "" + f_I + sf_iAN);
+        test("94000000-2000000", "" + f_I + sf_iM);
+        test("94000000-820130816", "" + f_I + f_lM);
+        test("94000000null", "" + f_I + sf_oAN);
+        test("9400000025000000", "" + f_I + s_I);
+        test("null-96.0", "" + f_IN + s_dM);
+        test("nullnull", "" + f_IN + s_oNtS);
+        test("null\u045176", "" + f_IN + f_strU);
+        test("null92", "" + f_IN + sf_strU2);
+        test("null51", "" + f_IN + sf_strU1);
+        test("nullnull", "" + f_IN + s_iAN);
+        test("null-54", "" + f_IN + f_bM);
+        test("null-87.0", "" + f_IN + f_fM);
+        test("nullnull", "" + f_IN + s_oAN);
+        test("null19", "" + f_IN + f_str);
+        test("null-41", "" + f_IN + sf_bM);
+        test("nullnull", "" + f_IN + sf_IN);
+        test("nullT", "" + f_IN + s_c);
+        test("null-42.0", "" + f_IN + sf_fM);
+        test("null25", "" + f_IN + s_b);
+        test("nullnull", "" + f_IN + f_oN);
+        test("null-1410065408", "" + f_IN + s_lM);
+        test("null8.0", "" + f_IN + s_d);
+        test("null55.0", "" + f_IN + s_f);
+        test("null97000000", "" + f_IN + s_i);
+        test("null-9900", "" + f_IN + f_sM);
+        test("null935228928", "" + f_IN + s_l);
+        test("null-8400", "" + f_IN + sf_sM);
+        test("nullC(82)", "" + f_IN + s_o);
+        test("nullnull", "" + f_IN + sf_oNtS);
+        test("nulltrue", "" + f_IN + s_bl);
+        test("null3900", "" + f_IN + s_s);
+        test("nullnull", "" + f_IN + sf_oN);
+        test("null94000000", "" + f_IN + f_I);
+        test("nullnull", "" + f_IN + f_IN);
+        test("nulltrue", "" + f_IN + sf_bl);
+        test("null5500", "" + f_IN + sf_s);
+        test("null-2900", "" + f_IN + s_sM);
+        test("null-194313216", "" + f_IN + sf_l);
+        test("null12", "" + f_IN + s_strU1);
+        test("nullC(87)", "" + f_IN + sf_o);
+        test("null91", "" + f_IN + s_strU2);
+        test("null21", "" + f_IN + f_strU1);
+        test("null18", "" + f_IN + f_strU2);
+        test("nullnull", "" + f_IN + f_iAN);
+        test("nullnull", "" + f_IN + s_oN);
+        test("null\u045180", "" + f_IN + s_strU);
+        test("nullC", "" + f_IN + sf_c);
+        test("null75", "" + f_IN + sf_str);
+        test("null-43", "" + f_IN + s_bM);
+        test("null80", "" + f_IN + sf_b);
+        test("nullnull", "" + f_IN + s_IN);
+        test("null-52.0", "" + f_IN + s_fM);
+        test("null75000000", "" + f_IN + sf_i);
+        test("null44", "" + f_IN + f_b);
+        test("null-1705032704", "" + f_IN + sf_lM);
+        test("nullnull", "" + f_IN + f_oAN);
+        test("null83.0", "" + f_IN + f_d);
+        test("nullI", "" + f_IN + f_c);
+        test("null94.0", "" + f_IN + f_f);
+        test("null12.0", "" + f_IN + sf_d);
+        test("null-99.0", "" + f_IN + f_dM);
+        test("null17.0", "" + f_IN + sf_f);
+        test("null-84.0", "" + f_IN + sf_dM);
+        test("null58000000", "" + f_IN + f_i);
+        test("null-55000000", "" + f_IN + f_iM);
+        test("null1460392448", "" + f_IN + f_l);
+        test("nullC(70)", "" + f_IN + f_o);
+        test("null\u04511", "" + f_IN + sf_strU);
+        test("null8000", "" + f_IN + f_s);
+        test("null18", "" + f_IN + s_str);
+        test("null-1000000", "" + f_IN + s_iM);
+        test("null1000000", "" + f_IN + sf_I);
+        test("nullnull", "" + f_IN + f_oNtS);
+        test("nullfalse", "" + f_IN + f_bl);
+        test("nullnull", "" + f_IN + sf_iAN);
+        test("null-2000000", "" + f_IN + sf_iM);
+        test("null-820130816", "" + f_IN + f_lM);
+        test("nullnull", "" + f_IN + sf_oAN);
+        test("null25000000", "" + f_IN + s_I);
+        test("true-96.0", "" + sf_bl + s_dM);
+        test("truenull", "" + sf_bl + s_oNtS);
+        test("true\u045176", "" + sf_bl + f_strU);
+        test("true92", "" + sf_bl + sf_strU2);
+        test("true51", "" + sf_bl + sf_strU1);
+        test("truenull", "" + sf_bl + s_iAN);
+        test("true-54", "" + sf_bl + f_bM);
+        test("true-87.0", "" + sf_bl + f_fM);
+        test("truenull", "" + sf_bl + s_oAN);
+        test("true19", "" + sf_bl + f_str);
+        test("true-41", "" + sf_bl + sf_bM);
+        test("truenull", "" + sf_bl + sf_IN);
+        test("trueT", "" + sf_bl + s_c);
+        test("true-42.0", "" + sf_bl + sf_fM);
+        test("true25", "" + sf_bl + s_b);
+        test("truenull", "" + sf_bl + f_oN);
+        test("true-1410065408", "" + sf_bl + s_lM);
+        test("true8.0", "" + sf_bl + s_d);
+        test("true55.0", "" + sf_bl + s_f);
+        test("true97000000", "" + sf_bl + s_i);
+        test("true-9900", "" + sf_bl + f_sM);
+        test("true935228928", "" + sf_bl + s_l);
+        test("true-8400", "" + sf_bl + sf_sM);
+        test("trueC(82)", "" + sf_bl + s_o);
+        test("truenull", "" + sf_bl + sf_oNtS);
+        test("truetrue", "" + sf_bl + s_bl);
+        test("true3900", "" + sf_bl + s_s);
+        test("truenull", "" + sf_bl + sf_oN);
+        test("true94000000", "" + sf_bl + f_I);
+        test("truenull", "" + sf_bl + f_IN);
+        test("truetrue", "" + sf_bl + sf_bl);
+        test("true5500", "" + sf_bl + sf_s);
+        test("true-2900", "" + sf_bl + s_sM);
+        test("true-194313216", "" + sf_bl + sf_l);
+        test("true12", "" + sf_bl + s_strU1);
+        test("trueC(87)", "" + sf_bl + sf_o);
+        test("true91", "" + sf_bl + s_strU2);
+        test("true21", "" + sf_bl + f_strU1);
+        test("true18", "" + sf_bl + f_strU2);
+        test("truenull", "" + sf_bl + f_iAN);
+        test("truenull", "" + sf_bl + s_oN);
+        test("true\u045180", "" + sf_bl + s_strU);
+        test("trueC", "" + sf_bl + sf_c);
+        test("true75", "" + sf_bl + sf_str);
+        test("true-43", "" + sf_bl + s_bM);
+        test("true80", "" + sf_bl + sf_b);
+        test("truenull", "" + sf_bl + s_IN);
+        test("true-52.0", "" + sf_bl + s_fM);
+        test("true75000000", "" + sf_bl + sf_i);
+        test("true44", "" + sf_bl + f_b);
+        test("true-1705032704", "" + sf_bl + sf_lM);
+        test("truenull", "" + sf_bl + f_oAN);
+        test("true83.0", "" + sf_bl + f_d);
+        test("trueI", "" + sf_bl + f_c);
+        test("true94.0", "" + sf_bl + f_f);
+        test("true12.0", "" + sf_bl + sf_d);
+        test("true-99.0", "" + sf_bl + f_dM);
+        test("true17.0", "" + sf_bl + sf_f);
+        test("true-84.0", "" + sf_bl + sf_dM);
+        test("true58000000", "" + sf_bl + f_i);
+        test("true-55000000", "" + sf_bl + f_iM);
+        test("true1460392448", "" + sf_bl + f_l);
+        test("trueC(70)", "" + sf_bl + f_o);
+        test("true\u04511", "" + sf_bl + sf_strU);
+        test("true8000", "" + sf_bl + f_s);
+        test("true18", "" + sf_bl + s_str);
+        test("true-1000000", "" + sf_bl + s_iM);
+        test("true1000000", "" + sf_bl + sf_I);
+        test("truenull", "" + sf_bl + f_oNtS);
+        test("truefalse", "" + sf_bl + f_bl);
+        test("truenull", "" + sf_bl + sf_iAN);
+        test("true-2000000", "" + sf_bl + sf_iM);
+        test("true-820130816", "" + sf_bl + f_lM);
+        test("truenull", "" + sf_bl + sf_oAN);
+        test("true25000000", "" + sf_bl + s_I);
+        test("5500-96.0", "" + sf_s + s_dM);
+        test("5500null", "" + sf_s + s_oNtS);
+        test("5500\u045176", "" + sf_s + f_strU);
+        test("550092", "" + sf_s + sf_strU2);
+        test("550051", "" + sf_s + sf_strU1);
+        test("5500null", "" + sf_s + s_iAN);
+        test("5500-54", "" + sf_s + f_bM);
+        test("5500-87.0", "" + sf_s + f_fM);
+        test("5500null", "" + sf_s + s_oAN);
+        test("550019", "" + sf_s + f_str);
+        test("5500-41", "" + sf_s + sf_bM);
+        test("5500null", "" + sf_s + sf_IN);
+        test("5500T", "" + sf_s + s_c);
+        test("5500-42.0", "" + sf_s + sf_fM);
+        test("550025", "" + sf_s + s_b);
+        test("5500null", "" + sf_s + f_oN);
+        test("5500-1410065408", "" + sf_s + s_lM);
+        test("55008.0", "" + sf_s + s_d);
+        test("550055.0", "" + sf_s + s_f);
+        test("550097000000", "" + sf_s + s_i);
+        test("5500-9900", "" + sf_s + f_sM);
+        test("5500935228928", "" + sf_s + s_l);
+        test("5500-8400", "" + sf_s + sf_sM);
+        test("5500C(82)", "" + sf_s + s_o);
+        test("5500null", "" + sf_s + sf_oNtS);
+        test("5500true", "" + sf_s + s_bl);
+        test("55003900", "" + sf_s + s_s);
+        test("5500null", "" + sf_s + sf_oN);
+        test("550094000000", "" + sf_s + f_I);
+        test("5500null", "" + sf_s + f_IN);
+        test("5500true", "" + sf_s + sf_bl);
+        test("55005500", "" + sf_s + sf_s);
+        test("5500-2900", "" + sf_s + s_sM);
+        test("5500-194313216", "" + sf_s + sf_l);
+        test("550012", "" + sf_s + s_strU1);
+        test("5500C(87)", "" + sf_s + sf_o);
+        test("550091", "" + sf_s + s_strU2);
+        test("550021", "" + sf_s + f_strU1);
+        test("550018", "" + sf_s + f_strU2);
+        test("5500null", "" + sf_s + f_iAN);
+        test("5500null", "" + sf_s + s_oN);
+        test("5500\u045180", "" + sf_s + s_strU);
+        test("5500C", "" + sf_s + sf_c);
+        test("550075", "" + sf_s + sf_str);
+        test("5500-43", "" + sf_s + s_bM);
+        test("550080", "" + sf_s + sf_b);
+        test("5500null", "" + sf_s + s_IN);
+        test("5500-52.0", "" + sf_s + s_fM);
+        test("550075000000", "" + sf_s + sf_i);
+        test("550044", "" + sf_s + f_b);
+        test("5500-1705032704", "" + sf_s + sf_lM);
+        test("5500null", "" + sf_s + f_oAN);
+        test("550083.0", "" + sf_s + f_d);
+        test("5500I", "" + sf_s + f_c);
+        test("550094.0", "" + sf_s + f_f);
+        test("550012.0", "" + sf_s + sf_d);
+        test("5500-99.0", "" + sf_s + f_dM);
+        test("550017.0", "" + sf_s + sf_f);
+        test("5500-84.0", "" + sf_s + sf_dM);
+        test("550058000000", "" + sf_s + f_i);
+        test("5500-55000000", "" + sf_s + f_iM);
+        test("55001460392448", "" + sf_s + f_l);
+        test("5500C(70)", "" + sf_s + f_o);
+        test("5500\u04511", "" + sf_s + sf_strU);
+        test("55008000", "" + sf_s + f_s);
+        test("550018", "" + sf_s + s_str);
+        test("5500-1000000", "" + sf_s + s_iM);
+        test("55001000000", "" + sf_s + sf_I);
+        test("5500null", "" + sf_s + f_oNtS);
+        test("5500false", "" + sf_s + f_bl);
+        test("5500null", "" + sf_s + sf_iAN);
+        test("5500-2000000", "" + sf_s + sf_iM);
+        test("5500-820130816", "" + sf_s + f_lM);
+        test("5500null", "" + sf_s + sf_oAN);
+        test("550025000000", "" + sf_s + s_I);
+        test("-2900-96.0", "" + s_sM + s_dM);
+        test("-2900null", "" + s_sM + s_oNtS);
+        test("-2900\u045176", "" + s_sM + f_strU);
+        test("-290092", "" + s_sM + sf_strU2);
+        test("-290051", "" + s_sM + sf_strU1);
+        test("-2900null", "" + s_sM + s_iAN);
+        test("-2900-54", "" + s_sM + f_bM);
+        test("-2900-87.0", "" + s_sM + f_fM);
+        test("-2900null", "" + s_sM + s_oAN);
+        test("-290019", "" + s_sM + f_str);
+        test("-2900-41", "" + s_sM + sf_bM);
+        test("-2900null", "" + s_sM + sf_IN);
+        test("-2900T", "" + s_sM + s_c);
+        test("-2900-42.0", "" + s_sM + sf_fM);
+        test("-290025", "" + s_sM + s_b);
+        test("-2900null", "" + s_sM + f_oN);
+        test("-2900-1410065408", "" + s_sM + s_lM);
+        test("-29008.0", "" + s_sM + s_d);
+        test("-290055.0", "" + s_sM + s_f);
+        test("-290097000000", "" + s_sM + s_i);
+        test("-2900-9900", "" + s_sM + f_sM);
+        test("-2900935228928", "" + s_sM + s_l);
+        test("-2900-8400", "" + s_sM + sf_sM);
+        test("-2900C(82)", "" + s_sM + s_o);
+        test("-2900null", "" + s_sM + sf_oNtS);
+        test("-2900true", "" + s_sM + s_bl);
+        test("-29003900", "" + s_sM + s_s);
+        test("-2900null", "" + s_sM + sf_oN);
+        test("-290094000000", "" + s_sM + f_I);
+        test("-2900null", "" + s_sM + f_IN);
+        test("-2900true", "" + s_sM + sf_bl);
+        test("-29005500", "" + s_sM + sf_s);
+        test("-2900-2900", "" + s_sM + s_sM);
+        test("-2900-194313216", "" + s_sM + sf_l);
+        test("-290012", "" + s_sM + s_strU1);
+        test("-2900C(87)", "" + s_sM + sf_o);
+        test("-290091", "" + s_sM + s_strU2);
+        test("-290021", "" + s_sM + f_strU1);
+        test("-290018", "" + s_sM + f_strU2);
+        test("-2900null", "" + s_sM + f_iAN);
+        test("-2900null", "" + s_sM + s_oN);
+        test("-2900\u045180", "" + s_sM + s_strU);
+        test("-2900C", "" + s_sM + sf_c);
+        test("-290075", "" + s_sM + sf_str);
+        test("-2900-43", "" + s_sM + s_bM);
+        test("-290080", "" + s_sM + sf_b);
+        test("-2900null", "" + s_sM + s_IN);
+        test("-2900-52.0", "" + s_sM + s_fM);
+        test("-290075000000", "" + s_sM + sf_i);
+        test("-290044", "" + s_sM + f_b);
+        test("-2900-1705032704", "" + s_sM + sf_lM);
+        test("-2900null", "" + s_sM + f_oAN);
+        test("-290083.0", "" + s_sM + f_d);
+        test("-2900I", "" + s_sM + f_c);
+        test("-290094.0", "" + s_sM + f_f);
+        test("-290012.0", "" + s_sM + sf_d);
+        test("-2900-99.0", "" + s_sM + f_dM);
+        test("-290017.0", "" + s_sM + sf_f);
+        test("-2900-84.0", "" + s_sM + sf_dM);
+        test("-290058000000", "" + s_sM + f_i);
+        test("-2900-55000000", "" + s_sM + f_iM);
+        test("-29001460392448", "" + s_sM + f_l);
+        test("-2900C(70)", "" + s_sM + f_o);
+        test("-2900\u04511", "" + s_sM + sf_strU);
+        test("-29008000", "" + s_sM + f_s);
+        test("-290018", "" + s_sM + s_str);
+        test("-2900-1000000", "" + s_sM + s_iM);
+        test("-29001000000", "" + s_sM + sf_I);
+        test("-2900null", "" + s_sM + f_oNtS);
+        test("-2900false", "" + s_sM + f_bl);
+        test("-2900null", "" + s_sM + sf_iAN);
+        test("-2900-2000000", "" + s_sM + sf_iM);
+        test("-2900-820130816", "" + s_sM + f_lM);
+        test("-2900null", "" + s_sM + sf_oAN);
+        test("-290025000000", "" + s_sM + s_I);
+        test("-194313216-96.0", "" + sf_l + s_dM);
+        test("-194313216null", "" + sf_l + s_oNtS);
+        test("-194313216\u045176", "" + sf_l + f_strU);
+        test("-19431321692", "" + sf_l + sf_strU2);
+        test("-19431321651", "" + sf_l + sf_strU1);
+        test("-194313216null", "" + sf_l + s_iAN);
+        test("-194313216-54", "" + sf_l + f_bM);
+        test("-194313216-87.0", "" + sf_l + f_fM);
+        test("-194313216null", "" + sf_l + s_oAN);
+        test("-19431321619", "" + sf_l + f_str);
+        test("-194313216-41", "" + sf_l + sf_bM);
+        test("-194313216null", "" + sf_l + sf_IN);
+        test("-194313216T", "" + sf_l + s_c);
+        test("-194313216-42.0", "" + sf_l + sf_fM);
+        test("-19431321625", "" + sf_l + s_b);
+        test("-194313216null", "" + sf_l + f_oN);
+        test("-194313216-1410065408", "" + sf_l + s_lM);
+        test("-1943132168.0", "" + sf_l + s_d);
+        test("-19431321655.0", "" + sf_l + s_f);
+        test("-19431321697000000", "" + sf_l + s_i);
+        test("-194313216-9900", "" + sf_l + f_sM);
+        test("-194313216935228928", "" + sf_l + s_l);
+        test("-194313216-8400", "" + sf_l + sf_sM);
+        test("-194313216C(82)", "" + sf_l + s_o);
+        test("-194313216null", "" + sf_l + sf_oNtS);
+        test("-194313216true", "" + sf_l + s_bl);
+        test("-1943132163900", "" + sf_l + s_s);
+        test("-194313216null", "" + sf_l + sf_oN);
+        test("-19431321694000000", "" + sf_l + f_I);
+        test("-194313216null", "" + sf_l + f_IN);
+        test("-194313216true", "" + sf_l + sf_bl);
+        test("-1943132165500", "" + sf_l + sf_s);
+        test("-194313216-2900", "" + sf_l + s_sM);
+        test("-194313216-194313216", "" + sf_l + sf_l);
+        test("-19431321612", "" + sf_l + s_strU1);
+        test("-194313216C(87)", "" + sf_l + sf_o);
+        test("-19431321691", "" + sf_l + s_strU2);
+        test("-19431321621", "" + sf_l + f_strU1);
+        test("-19431321618", "" + sf_l + f_strU2);
+        test("-194313216null", "" + sf_l + f_iAN);
+        test("-194313216null", "" + sf_l + s_oN);
+        test("-194313216\u045180", "" + sf_l + s_strU);
+        test("-194313216C", "" + sf_l + sf_c);
+        test("-19431321675", "" + sf_l + sf_str);
+        test("-194313216-43", "" + sf_l + s_bM);
+        test("-19431321680", "" + sf_l + sf_b);
+        test("-194313216null", "" + sf_l + s_IN);
+        test("-194313216-52.0", "" + sf_l + s_fM);
+        test("-19431321675000000", "" + sf_l + sf_i);
+        test("-19431321644", "" + sf_l + f_b);
+        test("-194313216-1705032704", "" + sf_l + sf_lM);
+        test("-194313216null", "" + sf_l + f_oAN);
+        test("-19431321683.0", "" + sf_l + f_d);
+        test("-194313216I", "" + sf_l + f_c);
+        test("-19431321694.0", "" + sf_l + f_f);
+        test("-19431321612.0", "" + sf_l + sf_d);
+        test("-194313216-99.0", "" + sf_l + f_dM);
+        test("-19431321617.0", "" + sf_l + sf_f);
+        test("-194313216-84.0", "" + sf_l + sf_dM);
+        test("-19431321658000000", "" + sf_l + f_i);
+        test("-194313216-55000000", "" + sf_l + f_iM);
+        test("-1943132161460392448", "" + sf_l + f_l);
+        test("-194313216C(70)", "" + sf_l + f_o);
+        test("-194313216\u04511", "" + sf_l + sf_strU);
+        test("-1943132168000", "" + sf_l + f_s);
+        test("-19431321618", "" + sf_l + s_str);
+        test("-194313216-1000000", "" + sf_l + s_iM);
+        test("-1943132161000000", "" + sf_l + sf_I);
+        test("-194313216null", "" + sf_l + f_oNtS);
+        test("-194313216false", "" + sf_l + f_bl);
+        test("-194313216null", "" + sf_l + sf_iAN);
+        test("-194313216-2000000", "" + sf_l + sf_iM);
+        test("-194313216-820130816", "" + sf_l + f_lM);
+        test("-194313216null", "" + sf_l + sf_oAN);
+        test("-19431321625000000", "" + sf_l + s_I);
+        test("12-96.0", "" + s_strU1 + s_dM);
+        test("12null", "" + s_strU1 + s_oNtS);
+        test("12\u045176", "" + s_strU1 + f_strU);
+        test("1292", "" + s_strU1 + sf_strU2);
+        test("1251", "" + s_strU1 + sf_strU1);
+        test("12null", "" + s_strU1 + s_iAN);
+        test("12-54", "" + s_strU1 + f_bM);
+        test("12-87.0", "" + s_strU1 + f_fM);
+        test("12null", "" + s_strU1 + s_oAN);
+        test("1219", "" + s_strU1 + f_str);
+        test("12-41", "" + s_strU1 + sf_bM);
+        test("12null", "" + s_strU1 + sf_IN);
+        test("12T", "" + s_strU1 + s_c);
+        test("12-42.0", "" + s_strU1 + sf_fM);
+        test("1225", "" + s_strU1 + s_b);
+        test("12null", "" + s_strU1 + f_oN);
+        test("12-1410065408", "" + s_strU1 + s_lM);
+        test("128.0", "" + s_strU1 + s_d);
+        test("1255.0", "" + s_strU1 + s_f);
+        test("1297000000", "" + s_strU1 + s_i);
+        test("12-9900", "" + s_strU1 + f_sM);
+        test("12935228928", "" + s_strU1 + s_l);
+        test("12-8400", "" + s_strU1 + sf_sM);
+        test("12C(82)", "" + s_strU1 + s_o);
+        test("12null", "" + s_strU1 + sf_oNtS);
+        test("12true", "" + s_strU1 + s_bl);
+        test("123900", "" + s_strU1 + s_s);
+        test("12null", "" + s_strU1 + sf_oN);
+        test("1294000000", "" + s_strU1 + f_I);
+        test("12null", "" + s_strU1 + f_IN);
+        test("12true", "" + s_strU1 + sf_bl);
+        test("125500", "" + s_strU1 + sf_s);
+        test("12-2900", "" + s_strU1 + s_sM);
+        test("12-194313216", "" + s_strU1 + sf_l);
+        test("1212", "" + s_strU1 + s_strU1);
+        test("12C(87)", "" + s_strU1 + sf_o);
+        test("1291", "" + s_strU1 + s_strU2);
+        test("1221", "" + s_strU1 + f_strU1);
+        test("1218", "" + s_strU1 + f_strU2);
+        test("12null", "" + s_strU1 + f_iAN);
+        test("12null", "" + s_strU1 + s_oN);
+        test("12\u045180", "" + s_strU1 + s_strU);
+        test("12C", "" + s_strU1 + sf_c);
+        test("1275", "" + s_strU1 + sf_str);
+        test("12-43", "" + s_strU1 + s_bM);
+        test("1280", "" + s_strU1 + sf_b);
+        test("12null", "" + s_strU1 + s_IN);
+        test("12-52.0", "" + s_strU1 + s_fM);
+        test("1275000000", "" + s_strU1 + sf_i);
+        test("1244", "" + s_strU1 + f_b);
+        test("12-1705032704", "" + s_strU1 + sf_lM);
+        test("12null", "" + s_strU1 + f_oAN);
+        test("1283.0", "" + s_strU1 + f_d);
+        test("12I", "" + s_strU1 + f_c);
+        test("1294.0", "" + s_strU1 + f_f);
+        test("1212.0", "" + s_strU1 + sf_d);
+        test("12-99.0", "" + s_strU1 + f_dM);
+        test("1217.0", "" + s_strU1 + sf_f);
+        test("12-84.0", "" + s_strU1 + sf_dM);
+        test("1258000000", "" + s_strU1 + f_i);
+        test("12-55000000", "" + s_strU1 + f_iM);
+        test("121460392448", "" + s_strU1 + f_l);
+        test("12C(70)", "" + s_strU1 + f_o);
+        test("12\u04511", "" + s_strU1 + sf_strU);
+        test("128000", "" + s_strU1 + f_s);
+        test("1218", "" + s_strU1 + s_str);
+        test("12-1000000", "" + s_strU1 + s_iM);
+        test("121000000", "" + s_strU1 + sf_I);
+        test("12null", "" + s_strU1 + f_oNtS);
+        test("12false", "" + s_strU1 + f_bl);
+        test("12null", "" + s_strU1 + sf_iAN);
+        test("12-2000000", "" + s_strU1 + sf_iM);
+        test("12-820130816", "" + s_strU1 + f_lM);
+        test("12null", "" + s_strU1 + sf_oAN);
+        test("1225000000", "" + s_strU1 + s_I);
+        test("C(87)-96.0", "" + sf_o + s_dM);
+        test("C(87)null", "" + sf_o + s_oNtS);
+        test("C(87)\u045176", "" + sf_o + f_strU);
+        test("C(87)92", "" + sf_o + sf_strU2);
+        test("C(87)51", "" + sf_o + sf_strU1);
+        test("C(87)null", "" + sf_o + s_iAN);
+        test("C(87)-54", "" + sf_o + f_bM);
+        test("C(87)-87.0", "" + sf_o + f_fM);
+        test("C(87)null", "" + sf_o + s_oAN);
+        test("C(87)19", "" + sf_o + f_str);
+        test("C(87)-41", "" + sf_o + sf_bM);
+        test("C(87)null", "" + sf_o + sf_IN);
+        test("C(87)T", "" + sf_o + s_c);
+        test("C(87)-42.0", "" + sf_o + sf_fM);
+        test("C(87)25", "" + sf_o + s_b);
+        test("C(87)null", "" + sf_o + f_oN);
+        test("C(87)-1410065408", "" + sf_o + s_lM);
+        test("C(87)8.0", "" + sf_o + s_d);
+        test("C(87)55.0", "" + sf_o + s_f);
+        test("C(87)97000000", "" + sf_o + s_i);
+        test("C(87)-9900", "" + sf_o + f_sM);
+        test("C(87)935228928", "" + sf_o + s_l);
+        test("C(87)-8400", "" + sf_o + sf_sM);
+        test("C(87)C(82)", "" + sf_o + s_o);
+        test("C(87)null", "" + sf_o + sf_oNtS);
+        test("C(87)true", "" + sf_o + s_bl);
+        test("C(87)3900", "" + sf_o + s_s);
+        test("C(87)null", "" + sf_o + sf_oN);
+        test("C(87)94000000", "" + sf_o + f_I);
+        test("C(87)null", "" + sf_o + f_IN);
+        test("C(87)true", "" + sf_o + sf_bl);
+        test("C(87)5500", "" + sf_o + sf_s);
+        test("C(87)-2900", "" + sf_o + s_sM);
+        test("C(87)-194313216", "" + sf_o + sf_l);
+        test("C(87)12", "" + sf_o + s_strU1);
+        test("C(87)C(87)", "" + sf_o + sf_o);
+        test("C(87)91", "" + sf_o + s_strU2);
+        test("C(87)21", "" + sf_o + f_strU1);
+        test("C(87)18", "" + sf_o + f_strU2);
+        test("C(87)null", "" + sf_o + f_iAN);
+        test("C(87)null", "" + sf_o + s_oN);
+        test("C(87)\u045180", "" + sf_o + s_strU);
+        test("C(87)C", "" + sf_o + sf_c);
+        test("C(87)75", "" + sf_o + sf_str);
+        test("C(87)-43", "" + sf_o + s_bM);
+        test("C(87)80", "" + sf_o + sf_b);
+        test("C(87)null", "" + sf_o + s_IN);
+        test("C(87)-52.0", "" + sf_o + s_fM);
+        test("C(87)75000000", "" + sf_o + sf_i);
+        test("C(87)44", "" + sf_o + f_b);
+        test("C(87)-1705032704", "" + sf_o + sf_lM);
+        test("C(87)null", "" + sf_o + f_oAN);
+        test("C(87)83.0", "" + sf_o + f_d);
+        test("C(87)I", "" + sf_o + f_c);
+        test("C(87)94.0", "" + sf_o + f_f);
+        test("C(87)12.0", "" + sf_o + sf_d);
+        test("C(87)-99.0", "" + sf_o + f_dM);
+        test("C(87)17.0", "" + sf_o + sf_f);
+        test("C(87)-84.0", "" + sf_o + sf_dM);
+        test("C(87)58000000", "" + sf_o + f_i);
+        test("C(87)-55000000", "" + sf_o + f_iM);
+        test("C(87)1460392448", "" + sf_o + f_l);
+        test("C(87)C(70)", "" + sf_o + f_o);
+        test("C(87)\u04511", "" + sf_o + sf_strU);
+        test("C(87)8000", "" + sf_o + f_s);
+        test("C(87)18", "" + sf_o + s_str);
+        test("C(87)-1000000", "" + sf_o + s_iM);
+        test("C(87)1000000", "" + sf_o + sf_I);
+        test("C(87)null", "" + sf_o + f_oNtS);
+        test("C(87)false", "" + sf_o + f_bl);
+        test("C(87)null", "" + sf_o + sf_iAN);
+        test("C(87)-2000000", "" + sf_o + sf_iM);
+        test("C(87)-820130816", "" + sf_o + f_lM);
+        test("C(87)null", "" + sf_o + sf_oAN);
+        test("C(87)25000000", "" + sf_o + s_I);
+        test("91-96.0", "" + s_strU2 + s_dM);
+        test("91null", "" + s_strU2 + s_oNtS);
+        test("91\u045176", "" + s_strU2 + f_strU);
+        test("9192", "" + s_strU2 + sf_strU2);
+        test("9151", "" + s_strU2 + sf_strU1);
+        test("91null", "" + s_strU2 + s_iAN);
+        test("91-54", "" + s_strU2 + f_bM);
+        test("91-87.0", "" + s_strU2 + f_fM);
+        test("91null", "" + s_strU2 + s_oAN);
+        test("9119", "" + s_strU2 + f_str);
+        test("91-41", "" + s_strU2 + sf_bM);
+        test("91null", "" + s_strU2 + sf_IN);
+        test("91T", "" + s_strU2 + s_c);
+        test("91-42.0", "" + s_strU2 + sf_fM);
+        test("9125", "" + s_strU2 + s_b);
+        test("91null", "" + s_strU2 + f_oN);
+        test("91-1410065408", "" + s_strU2 + s_lM);
+        test("918.0", "" + s_strU2 + s_d);
+        test("9155.0", "" + s_strU2 + s_f);
+        test("9197000000", "" + s_strU2 + s_i);
+        test("91-9900", "" + s_strU2 + f_sM);
+        test("91935228928", "" + s_strU2 + s_l);
+        test("91-8400", "" + s_strU2 + sf_sM);
+        test("91C(82)", "" + s_strU2 + s_o);
+        test("91null", "" + s_strU2 + sf_oNtS);
+        test("91true", "" + s_strU2 + s_bl);
+        test("913900", "" + s_strU2 + s_s);
+        test("91null", "" + s_strU2 + sf_oN);
+        test("9194000000", "" + s_strU2 + f_I);
+        test("91null", "" + s_strU2 + f_IN);
+        test("91true", "" + s_strU2 + sf_bl);
+        test("915500", "" + s_strU2 + sf_s);
+        test("91-2900", "" + s_strU2 + s_sM);
+        test("91-194313216", "" + s_strU2 + sf_l);
+        test("9112", "" + s_strU2 + s_strU1);
+        test("91C(87)", "" + s_strU2 + sf_o);
+        test("9191", "" + s_strU2 + s_strU2);
+        test("9121", "" + s_strU2 + f_strU1);
+        test("9118", "" + s_strU2 + f_strU2);
+        test("91null", "" + s_strU2 + f_iAN);
+        test("91null", "" + s_strU2 + s_oN);
+        test("91\u045180", "" + s_strU2 + s_strU);
+        test("91C", "" + s_strU2 + sf_c);
+        test("9175", "" + s_strU2 + sf_str);
+        test("91-43", "" + s_strU2 + s_bM);
+        test("9180", "" + s_strU2 + sf_b);
+        test("91null", "" + s_strU2 + s_IN);
+        test("91-52.0", "" + s_strU2 + s_fM);
+        test("9175000000", "" + s_strU2 + sf_i);
+        test("9144", "" + s_strU2 + f_b);
+        test("91-1705032704", "" + s_strU2 + sf_lM);
+        test("91null", "" + s_strU2 + f_oAN);
+        test("9183.0", "" + s_strU2 + f_d);
+        test("91I", "" + s_strU2 + f_c);
+        test("9194.0", "" + s_strU2 + f_f);
+        test("9112.0", "" + s_strU2 + sf_d);
+        test("91-99.0", "" + s_strU2 + f_dM);
+        test("9117.0", "" + s_strU2 + sf_f);
+        test("91-84.0", "" + s_strU2 + sf_dM);
+        test("9158000000", "" + s_strU2 + f_i);
+        test("91-55000000", "" + s_strU2 + f_iM);
+        test("911460392448", "" + s_strU2 + f_l);
+        test("91C(70)", "" + s_strU2 + f_o);
+        test("91\u04511", "" + s_strU2 + sf_strU);
+        test("918000", "" + s_strU2 + f_s);
+        test("9118", "" + s_strU2 + s_str);
+        test("91-1000000", "" + s_strU2 + s_iM);
+        test("911000000", "" + s_strU2 + sf_I);
+        test("91null", "" + s_strU2 + f_oNtS);
+        test("91false", "" + s_strU2 + f_bl);
+        test("91null", "" + s_strU2 + sf_iAN);
+        test("91-2000000", "" + s_strU2 + sf_iM);
+        test("91-820130816", "" + s_strU2 + f_lM);
+        test("91null", "" + s_strU2 + sf_oAN);
+        test("9125000000", "" + s_strU2 + s_I);
+        test("21-96.0", "" + f_strU1 + s_dM);
+        test("21null", "" + f_strU1 + s_oNtS);
+        test("21\u045176", "" + f_strU1 + f_strU);
+        test("2192", "" + f_strU1 + sf_strU2);
+        test("2151", "" + f_strU1 + sf_strU1);
+        test("21null", "" + f_strU1 + s_iAN);
+        test("21-54", "" + f_strU1 + f_bM);
+        test("21-87.0", "" + f_strU1 + f_fM);
+        test("21null", "" + f_strU1 + s_oAN);
+        test("2119", "" + f_strU1 + f_str);
+        test("21-41", "" + f_strU1 + sf_bM);
+        test("21null", "" + f_strU1 + sf_IN);
+        test("21T", "" + f_strU1 + s_c);
+        test("21-42.0", "" + f_strU1 + sf_fM);
+        test("2125", "" + f_strU1 + s_b);
+        test("21null", "" + f_strU1 + f_oN);
+        test("21-1410065408", "" + f_strU1 + s_lM);
+        test("218.0", "" + f_strU1 + s_d);
+        test("2155.0", "" + f_strU1 + s_f);
+        test("2197000000", "" + f_strU1 + s_i);
+        test("21-9900", "" + f_strU1 + f_sM);
+        test("21935228928", "" + f_strU1 + s_l);
+        test("21-8400", "" + f_strU1 + sf_sM);
+        test("21C(82)", "" + f_strU1 + s_o);
+        test("21null", "" + f_strU1 + sf_oNtS);
+        test("21true", "" + f_strU1 + s_bl);
+        test("213900", "" + f_strU1 + s_s);
+        test("21null", "" + f_strU1 + sf_oN);
+        test("2194000000", "" + f_strU1 + f_I);
+        test("21null", "" + f_strU1 + f_IN);
+        test("21true", "" + f_strU1 + sf_bl);
+        test("215500", "" + f_strU1 + sf_s);
+        test("21-2900", "" + f_strU1 + s_sM);
+        test("21-194313216", "" + f_strU1 + sf_l);
+        test("2112", "" + f_strU1 + s_strU1);
+        test("21C(87)", "" + f_strU1 + sf_o);
+        test("2191", "" + f_strU1 + s_strU2);
+        test("2121", "" + f_strU1 + f_strU1);
+        test("2118", "" + f_strU1 + f_strU2);
+        test("21null", "" + f_strU1 + f_iAN);
+        test("21null", "" + f_strU1 + s_oN);
+        test("21\u045180", "" + f_strU1 + s_strU);
+        test("21C", "" + f_strU1 + sf_c);
+        test("2175", "" + f_strU1 + sf_str);
+        test("21-43", "" + f_strU1 + s_bM);
+        test("2180", "" + f_strU1 + sf_b);
+        test("21null", "" + f_strU1 + s_IN);
+        test("21-52.0", "" + f_strU1 + s_fM);
+        test("2175000000", "" + f_strU1 + sf_i);
+        test("2144", "" + f_strU1 + f_b);
+        test("21-1705032704", "" + f_strU1 + sf_lM);
+        test("21null", "" + f_strU1 + f_oAN);
+        test("2183.0", "" + f_strU1 + f_d);
+        test("21I", "" + f_strU1 + f_c);
+        test("2194.0", "" + f_strU1 + f_f);
+        test("2112.0", "" + f_strU1 + sf_d);
+        test("21-99.0", "" + f_strU1 + f_dM);
+        test("2117.0", "" + f_strU1 + sf_f);
+        test("21-84.0", "" + f_strU1 + sf_dM);
+        test("2158000000", "" + f_strU1 + f_i);
+        test("21-55000000", "" + f_strU1 + f_iM);
+        test("211460392448", "" + f_strU1 + f_l);
+        test("21C(70)", "" + f_strU1 + f_o);
+        test("21\u04511", "" + f_strU1 + sf_strU);
+        test("218000", "" + f_strU1 + f_s);
+        test("2118", "" + f_strU1 + s_str);
+        test("21-1000000", "" + f_strU1 + s_iM);
+        test("211000000", "" + f_strU1 + sf_I);
+        test("21null", "" + f_strU1 + f_oNtS);
+        test("21false", "" + f_strU1 + f_bl);
+        test("21null", "" + f_strU1 + sf_iAN);
+        test("21-2000000", "" + f_strU1 + sf_iM);
+        test("21-820130816", "" + f_strU1 + f_lM);
+        test("21null", "" + f_strU1 + sf_oAN);
+        test("2125000000", "" + f_strU1 + s_I);
+        test("18-96.0", "" + f_strU2 + s_dM);
+        test("18null", "" + f_strU2 + s_oNtS);
+        test("18\u045176", "" + f_strU2 + f_strU);
+        test("1892", "" + f_strU2 + sf_strU2);
+        test("1851", "" + f_strU2 + sf_strU1);
+        test("18null", "" + f_strU2 + s_iAN);
+        test("18-54", "" + f_strU2 + f_bM);
+        test("18-87.0", "" + f_strU2 + f_fM);
+        test("18null", "" + f_strU2 + s_oAN);
+        test("1819", "" + f_strU2 + f_str);
+        test("18-41", "" + f_strU2 + sf_bM);
+        test("18null", "" + f_strU2 + sf_IN);
+        test("18T", "" + f_strU2 + s_c);
+        test("18-42.0", "" + f_strU2 + sf_fM);
+        test("1825", "" + f_strU2 + s_b);
+        test("18null", "" + f_strU2 + f_oN);
+        test("18-1410065408", "" + f_strU2 + s_lM);
+        test("188.0", "" + f_strU2 + s_d);
+        test("1855.0", "" + f_strU2 + s_f);
+        test("1897000000", "" + f_strU2 + s_i);
+        test("18-9900", "" + f_strU2 + f_sM);
+        test("18935228928", "" + f_strU2 + s_l);
+        test("18-8400", "" + f_strU2 + sf_sM);
+        test("18C(82)", "" + f_strU2 + s_o);
+        test("18null", "" + f_strU2 + sf_oNtS);
+        test("18true", "" + f_strU2 + s_bl);
+        test("183900", "" + f_strU2 + s_s);
+        test("18null", "" + f_strU2 + sf_oN);
+        test("1894000000", "" + f_strU2 + f_I);
+        test("18null", "" + f_strU2 + f_IN);
+        test("18true", "" + f_strU2 + sf_bl);
+        test("185500", "" + f_strU2 + sf_s);
+        test("18-2900", "" + f_strU2 + s_sM);
+        test("18-194313216", "" + f_strU2 + sf_l);
+        test("1812", "" + f_strU2 + s_strU1);
+        test("18C(87)", "" + f_strU2 + sf_o);
+        test("1891", "" + f_strU2 + s_strU2);
+        test("1821", "" + f_strU2 + f_strU1);
+        test("1818", "" + f_strU2 + f_strU2);
+        test("18null", "" + f_strU2 + f_iAN);
+        test("18null", "" + f_strU2 + s_oN);
+        test("18\u045180", "" + f_strU2 + s_strU);
+        test("18C", "" + f_strU2 + sf_c);
+        test("1875", "" + f_strU2 + sf_str);
+        test("18-43", "" + f_strU2 + s_bM);
+        test("1880", "" + f_strU2 + sf_b);
+        test("18null", "" + f_strU2 + s_IN);
+        test("18-52.0", "" + f_strU2 + s_fM);
+        test("1875000000", "" + f_strU2 + sf_i);
+        test("1844", "" + f_strU2 + f_b);
+        test("18-1705032704", "" + f_strU2 + sf_lM);
+        test("18null", "" + f_strU2 + f_oAN);
+        test("1883.0", "" + f_strU2 + f_d);
+        test("18I", "" + f_strU2 + f_c);
+        test("1894.0", "" + f_strU2 + f_f);
+        test("1812.0", "" + f_strU2 + sf_d);
+        test("18-99.0", "" + f_strU2 + f_dM);
+        test("1817.0", "" + f_strU2 + sf_f);
+        test("18-84.0", "" + f_strU2 + sf_dM);
+        test("1858000000", "" + f_strU2 + f_i);
+        test("18-55000000", "" + f_strU2 + f_iM);
+        test("181460392448", "" + f_strU2 + f_l);
+        test("18C(70)", "" + f_strU2 + f_o);
+        test("18\u04511", "" + f_strU2 + sf_strU);
+        test("188000", "" + f_strU2 + f_s);
+        test("1818", "" + f_strU2 + s_str);
+        test("18-1000000", "" + f_strU2 + s_iM);
+        test("181000000", "" + f_strU2 + sf_I);
+        test("18null", "" + f_strU2 + f_oNtS);
+        test("18false", "" + f_strU2 + f_bl);
+        test("18null", "" + f_strU2 + sf_iAN);
+        test("18-2000000", "" + f_strU2 + sf_iM);
+        test("18-820130816", "" + f_strU2 + f_lM);
+        test("18null", "" + f_strU2 + sf_oAN);
+        test("1825000000", "" + f_strU2 + s_I);
+    }
+
+    public void run3() {
+        test("null-96.0", "" + f_iAN + s_dM);
+        test("nullnull", "" + f_iAN + s_oNtS);
+        test("null\u045176", "" + f_iAN + f_strU);
+        test("null92", "" + f_iAN + sf_strU2);
+        test("null51", "" + f_iAN + sf_strU1);
+        test("nullnull", "" + f_iAN + s_iAN);
+        test("null-54", "" + f_iAN + f_bM);
+        test("null-87.0", "" + f_iAN + f_fM);
+        test("nullnull", "" + f_iAN + s_oAN);
+        test("null19", "" + f_iAN + f_str);
+        test("null-41", "" + f_iAN + sf_bM);
+        test("nullnull", "" + f_iAN + sf_IN);
+        test("nullT", "" + f_iAN + s_c);
+        test("null-42.0", "" + f_iAN + sf_fM);
+        test("null25", "" + f_iAN + s_b);
+        test("nullnull", "" + f_iAN + f_oN);
+        test("null-1410065408", "" + f_iAN + s_lM);
+        test("null8.0", "" + f_iAN + s_d);
+        test("null55.0", "" + f_iAN + s_f);
+        test("null97000000", "" + f_iAN + s_i);
+        test("null-9900", "" + f_iAN + f_sM);
+        test("null935228928", "" + f_iAN + s_l);
+        test("null-8400", "" + f_iAN + sf_sM);
+        test("nullC(82)", "" + f_iAN + s_o);
+        test("nullnull", "" + f_iAN + sf_oNtS);
+        test("nulltrue", "" + f_iAN + s_bl);
+        test("null3900", "" + f_iAN + s_s);
+        test("nullnull", "" + f_iAN + sf_oN);
+        test("null94000000", "" + f_iAN + f_I);
+        test("nullnull", "" + f_iAN + f_IN);
+        test("nulltrue", "" + f_iAN + sf_bl);
+        test("null5500", "" + f_iAN + sf_s);
+        test("null-2900", "" + f_iAN + s_sM);
+        test("null-194313216", "" + f_iAN + sf_l);
+        test("null12", "" + f_iAN + s_strU1);
+        test("nullC(87)", "" + f_iAN + sf_o);
+        test("null91", "" + f_iAN + s_strU2);
+        test("null21", "" + f_iAN + f_strU1);
+        test("null18", "" + f_iAN + f_strU2);
+        test("nullnull", "" + f_iAN + f_iAN);
+        test("nullnull", "" + f_iAN + s_oN);
+        test("null\u045180", "" + f_iAN + s_strU);
+        test("nullC", "" + f_iAN + sf_c);
+        test("null75", "" + f_iAN + sf_str);
+        test("null-43", "" + f_iAN + s_bM);
+        test("null80", "" + f_iAN + sf_b);
+        test("nullnull", "" + f_iAN + s_IN);
+        test("null-52.0", "" + f_iAN + s_fM);
+        test("null75000000", "" + f_iAN + sf_i);
+        test("null44", "" + f_iAN + f_b);
+        test("null-1705032704", "" + f_iAN + sf_lM);
+        test("nullnull", "" + f_iAN + f_oAN);
+        test("null83.0", "" + f_iAN + f_d);
+        test("nullI", "" + f_iAN + f_c);
+        test("null94.0", "" + f_iAN + f_f);
+        test("null12.0", "" + f_iAN + sf_d);
+        test("null-99.0", "" + f_iAN + f_dM);
+        test("null17.0", "" + f_iAN + sf_f);
+        test("null-84.0", "" + f_iAN + sf_dM);
+        test("null58000000", "" + f_iAN + f_i);
+        test("null-55000000", "" + f_iAN + f_iM);
+        test("null1460392448", "" + f_iAN + f_l);
+        test("nullC(70)", "" + f_iAN + f_o);
+        test("null\u04511", "" + f_iAN + sf_strU);
+        test("null8000", "" + f_iAN + f_s);
+        test("null18", "" + f_iAN + s_str);
+        test("null-1000000", "" + f_iAN + s_iM);
+        test("null1000000", "" + f_iAN + sf_I);
+        test("nullnull", "" + f_iAN + f_oNtS);
+        test("nullfalse", "" + f_iAN + f_bl);
+        test("nullnull", "" + f_iAN + sf_iAN);
+        test("null-2000000", "" + f_iAN + sf_iM);
+        test("null-820130816", "" + f_iAN + f_lM);
+        test("nullnull", "" + f_iAN + sf_oAN);
+        test("null25000000", "" + f_iAN + s_I);
+        test("null-96.0", "" + s_oN + s_dM);
+        test("nullnull", "" + s_oN + s_oNtS);
+        test("null\u045176", "" + s_oN + f_strU);
+        test("null92", "" + s_oN + sf_strU2);
+        test("null51", "" + s_oN + sf_strU1);
+        test("nullnull", "" + s_oN + s_iAN);
+        test("null-54", "" + s_oN + f_bM);
+        test("null-87.0", "" + s_oN + f_fM);
+        test("nullnull", "" + s_oN + s_oAN);
+        test("null19", "" + s_oN + f_str);
+        test("null-41", "" + s_oN + sf_bM);
+        test("nullnull", "" + s_oN + sf_IN);
+        test("nullT", "" + s_oN + s_c);
+        test("null-42.0", "" + s_oN + sf_fM);
+        test("null25", "" + s_oN + s_b);
+        test("nullnull", "" + s_oN + f_oN);
+        test("null-1410065408", "" + s_oN + s_lM);
+        test("null8.0", "" + s_oN + s_d);
+        test("null55.0", "" + s_oN + s_f);
+        test("null97000000", "" + s_oN + s_i);
+        test("null-9900", "" + s_oN + f_sM);
+        test("null935228928", "" + s_oN + s_l);
+        test("null-8400", "" + s_oN + sf_sM);
+        test("nullC(82)", "" + s_oN + s_o);
+        test("nullnull", "" + s_oN + sf_oNtS);
+        test("nulltrue", "" + s_oN + s_bl);
+        test("null3900", "" + s_oN + s_s);
+        test("nullnull", "" + s_oN + sf_oN);
+        test("null94000000", "" + s_oN + f_I);
+        test("nullnull", "" + s_oN + f_IN);
+        test("nulltrue", "" + s_oN + sf_bl);
+        test("null5500", "" + s_oN + sf_s);
+        test("null-2900", "" + s_oN + s_sM);
+        test("null-194313216", "" + s_oN + sf_l);
+        test("null12", "" + s_oN + s_strU1);
+        test("nullC(87)", "" + s_oN + sf_o);
+        test("null91", "" + s_oN + s_strU2);
+        test("null21", "" + s_oN + f_strU1);
+        test("null18", "" + s_oN + f_strU2);
+        test("nullnull", "" + s_oN + f_iAN);
+        test("nullnull", "" + s_oN + s_oN);
+        test("null\u045180", "" + s_oN + s_strU);
+        test("nullC", "" + s_oN + sf_c);
+        test("null75", "" + s_oN + sf_str);
+        test("null-43", "" + s_oN + s_bM);
+        test("null80", "" + s_oN + sf_b);
+        test("nullnull", "" + s_oN + s_IN);
+        test("null-52.0", "" + s_oN + s_fM);
+        test("null75000000", "" + s_oN + sf_i);
+        test("null44", "" + s_oN + f_b);
+        test("null-1705032704", "" + s_oN + sf_lM);
+        test("nullnull", "" + s_oN + f_oAN);
+        test("null83.0", "" + s_oN + f_d);
+        test("nullI", "" + s_oN + f_c);
+        test("null94.0", "" + s_oN + f_f);
+        test("null12.0", "" + s_oN + sf_d);
+        test("null-99.0", "" + s_oN + f_dM);
+        test("null17.0", "" + s_oN + sf_f);
+        test("null-84.0", "" + s_oN + sf_dM);
+        test("null58000000", "" + s_oN + f_i);
+        test("null-55000000", "" + s_oN + f_iM);
+        test("null1460392448", "" + s_oN + f_l);
+        test("nullC(70)", "" + s_oN + f_o);
+        test("null\u04511", "" + s_oN + sf_strU);
+        test("null8000", "" + s_oN + f_s);
+        test("null18", "" + s_oN + s_str);
+        test("null-1000000", "" + s_oN + s_iM);
+        test("null1000000", "" + s_oN + sf_I);
+        test("nullnull", "" + s_oN + f_oNtS);
+        test("nullfalse", "" + s_oN + f_bl);
+        test("nullnull", "" + s_oN + sf_iAN);
+        test("null-2000000", "" + s_oN + sf_iM);
+        test("null-820130816", "" + s_oN + f_lM);
+        test("nullnull", "" + s_oN + sf_oAN);
+        test("null25000000", "" + s_oN + s_I);
+        test("\u045180-96.0", "" + s_strU + s_dM);
+        test("\u045180null", "" + s_strU + s_oNtS);
+        test("\u045180\u045176", "" + s_strU + f_strU);
+        test("\u04518092", "" + s_strU + sf_strU2);
+        test("\u04518051", "" + s_strU + sf_strU1);
+        test("\u045180null", "" + s_strU + s_iAN);
+        test("\u045180-54", "" + s_strU + f_bM);
+        test("\u045180-87.0", "" + s_strU + f_fM);
+        test("\u045180null", "" + s_strU + s_oAN);
+        test("\u04518019", "" + s_strU + f_str);
+        test("\u045180-41", "" + s_strU + sf_bM);
+        test("\u045180null", "" + s_strU + sf_IN);
+        test("\u045180T", "" + s_strU + s_c);
+        test("\u045180-42.0", "" + s_strU + sf_fM);
+        test("\u04518025", "" + s_strU + s_b);
+        test("\u045180null", "" + s_strU + f_oN);
+        test("\u045180-1410065408", "" + s_strU + s_lM);
+        test("\u0451808.0", "" + s_strU + s_d);
+        test("\u04518055.0", "" + s_strU + s_f);
+        test("\u04518097000000", "" + s_strU + s_i);
+        test("\u045180-9900", "" + s_strU + f_sM);
+        test("\u045180935228928", "" + s_strU + s_l);
+        test("\u045180-8400", "" + s_strU + sf_sM);
+        test("\u045180C(82)", "" + s_strU + s_o);
+        test("\u045180null", "" + s_strU + sf_oNtS);
+        test("\u045180true", "" + s_strU + s_bl);
+        test("\u0451803900", "" + s_strU + s_s);
+        test("\u045180null", "" + s_strU + sf_oN);
+        test("\u04518094000000", "" + s_strU + f_I);
+        test("\u045180null", "" + s_strU + f_IN);
+        test("\u045180true", "" + s_strU + sf_bl);
+        test("\u0451805500", "" + s_strU + sf_s);
+        test("\u045180-2900", "" + s_strU + s_sM);
+        test("\u045180-194313216", "" + s_strU + sf_l);
+        test("\u04518012", "" + s_strU + s_strU1);
+        test("\u045180C(87)", "" + s_strU + sf_o);
+        test("\u04518091", "" + s_strU + s_strU2);
+        test("\u04518021", "" + s_strU + f_strU1);
+        test("\u04518018", "" + s_strU + f_strU2);
+        test("\u045180null", "" + s_strU + f_iAN);
+        test("\u045180null", "" + s_strU + s_oN);
+        test("\u045180\u045180", "" + s_strU + s_strU);
+        test("\u045180C", "" + s_strU + sf_c);
+        test("\u04518075", "" + s_strU + sf_str);
+        test("\u045180-43", "" + s_strU + s_bM);
+        test("\u04518080", "" + s_strU + sf_b);
+        test("\u045180null", "" + s_strU + s_IN);
+        test("\u045180-52.0", "" + s_strU + s_fM);
+        test("\u04518075000000", "" + s_strU + sf_i);
+        test("\u04518044", "" + s_strU + f_b);
+        test("\u045180-1705032704", "" + s_strU + sf_lM);
+        test("\u045180null", "" + s_strU + f_oAN);
+        test("\u04518083.0", "" + s_strU + f_d);
+        test("\u045180I", "" + s_strU + f_c);
+        test("\u04518094.0", "" + s_strU + f_f);
+        test("\u04518012.0", "" + s_strU + sf_d);
+        test("\u045180-99.0", "" + s_strU + f_dM);
+        test("\u04518017.0", "" + s_strU + sf_f);
+        test("\u045180-84.0", "" + s_strU + sf_dM);
+        test("\u04518058000000", "" + s_strU + f_i);
+        test("\u045180-55000000", "" + s_strU + f_iM);
+        test("\u0451801460392448", "" + s_strU + f_l);
+        test("\u045180C(70)", "" + s_strU + f_o);
+        test("\u045180\u04511", "" + s_strU + sf_strU);
+        test("\u0451808000", "" + s_strU + f_s);
+        test("\u04518018", "" + s_strU + s_str);
+        test("\u045180-1000000", "" + s_strU + s_iM);
+        test("\u0451801000000", "" + s_strU + sf_I);
+        test("\u045180null", "" + s_strU + f_oNtS);
+        test("\u045180false", "" + s_strU + f_bl);
+        test("\u045180null", "" + s_strU + sf_iAN);
+        test("\u045180-2000000", "" + s_strU + sf_iM);
+        test("\u045180-820130816", "" + s_strU + f_lM);
+        test("\u045180null", "" + s_strU + sf_oAN);
+        test("\u04518025000000", "" + s_strU + s_I);
+        test("C-96.0", "" + sf_c + s_dM);
+        test("Cnull", "" + sf_c + s_oNtS);
+        test("C\u045176", "" + sf_c + f_strU);
+        test("C92", "" + sf_c + sf_strU2);
+        test("C51", "" + sf_c + sf_strU1);
+        test("Cnull", "" + sf_c + s_iAN);
+        test("C-54", "" + sf_c + f_bM);
+        test("C-87.0", "" + sf_c + f_fM);
+        test("Cnull", "" + sf_c + s_oAN);
+        test("C19", "" + sf_c + f_str);
+        test("C-41", "" + sf_c + sf_bM);
+        test("Cnull", "" + sf_c + sf_IN);
+        test("CT", "" + sf_c + s_c);
+        test("C-42.0", "" + sf_c + sf_fM);
+        test("C25", "" + sf_c + s_b);
+        test("Cnull", "" + sf_c + f_oN);
+        test("C-1410065408", "" + sf_c + s_lM);
+        test("C8.0", "" + sf_c + s_d);
+        test("C55.0", "" + sf_c + s_f);
+        test("C97000000", "" + sf_c + s_i);
+        test("C-9900", "" + sf_c + f_sM);
+        test("C935228928", "" + sf_c + s_l);
+        test("C-8400", "" + sf_c + sf_sM);
+        test("CC(82)", "" + sf_c + s_o);
+        test("Cnull", "" + sf_c + sf_oNtS);
+        test("Ctrue", "" + sf_c + s_bl);
+        test("C3900", "" + sf_c + s_s);
+        test("Cnull", "" + sf_c + sf_oN);
+        test("C94000000", "" + sf_c + f_I);
+        test("Cnull", "" + sf_c + f_IN);
+        test("Ctrue", "" + sf_c + sf_bl);
+        test("C5500", "" + sf_c + sf_s);
+        test("C-2900", "" + sf_c + s_sM);
+        test("C-194313216", "" + sf_c + sf_l);
+        test("C12", "" + sf_c + s_strU1);
+        test("CC(87)", "" + sf_c + sf_o);
+        test("C91", "" + sf_c + s_strU2);
+        test("C21", "" + sf_c + f_strU1);
+        test("C18", "" + sf_c + f_strU2);
+        test("Cnull", "" + sf_c + f_iAN);
+        test("Cnull", "" + sf_c + s_oN);
+        test("C\u045180", "" + sf_c + s_strU);
+        test("CC", "" + sf_c + sf_c);
+        test("C75", "" + sf_c + sf_str);
+        test("C-43", "" + sf_c + s_bM);
+        test("C80", "" + sf_c + sf_b);
+        test("Cnull", "" + sf_c + s_IN);
+        test("C-52.0", "" + sf_c + s_fM);
+        test("C75000000", "" + sf_c + sf_i);
+        test("C44", "" + sf_c + f_b);
+        test("C-1705032704", "" + sf_c + sf_lM);
+        test("Cnull", "" + sf_c + f_oAN);
+        test("C83.0", "" + sf_c + f_d);
+        test("CI", "" + sf_c + f_c);
+        test("C94.0", "" + sf_c + f_f);
+        test("C12.0", "" + sf_c + sf_d);
+        test("C-99.0", "" + sf_c + f_dM);
+        test("C17.0", "" + sf_c + sf_f);
+        test("C-84.0", "" + sf_c + sf_dM);
+        test("C58000000", "" + sf_c + f_i);
+        test("C-55000000", "" + sf_c + f_iM);
+        test("C1460392448", "" + sf_c + f_l);
+        test("CC(70)", "" + sf_c + f_o);
+        test("C\u04511", "" + sf_c + sf_strU);
+        test("C8000", "" + sf_c + f_s);
+        test("C18", "" + sf_c + s_str);
+        test("C-1000000", "" + sf_c + s_iM);
+        test("C1000000", "" + sf_c + sf_I);
+        test("Cnull", "" + sf_c + f_oNtS);
+        test("Cfalse", "" + sf_c + f_bl);
+        test("Cnull", "" + sf_c + sf_iAN);
+        test("C-2000000", "" + sf_c + sf_iM);
+        test("C-820130816", "" + sf_c + f_lM);
+        test("Cnull", "" + sf_c + sf_oAN);
+        test("C25000000", "" + sf_c + s_I);
+        test("75-96.0", "" + sf_str + s_dM);
+        test("75null", "" + sf_str + s_oNtS);
+        test("75\u045176", "" + sf_str + f_strU);
+        test("7592", "" + sf_str + sf_strU2);
+        test("7551", "" + sf_str + sf_strU1);
+        test("75null", "" + sf_str + s_iAN);
+        test("75-54", "" + sf_str + f_bM);
+        test("75-87.0", "" + sf_str + f_fM);
+        test("75null", "" + sf_str + s_oAN);
+        test("7519", "" + sf_str + f_str);
+        test("75-41", "" + sf_str + sf_bM);
+        test("75null", "" + sf_str + sf_IN);
+        test("75T", "" + sf_str + s_c);
+        test("75-42.0", "" + sf_str + sf_fM);
+        test("7525", "" + sf_str + s_b);
+        test("75null", "" + sf_str + f_oN);
+        test("75-1410065408", "" + sf_str + s_lM);
+        test("758.0", "" + sf_str + s_d);
+        test("7555.0", "" + sf_str + s_f);
+        test("7597000000", "" + sf_str + s_i);
+        test("75-9900", "" + sf_str + f_sM);
+        test("75935228928", "" + sf_str + s_l);
+        test("75-8400", "" + sf_str + sf_sM);
+        test("75C(82)", "" + sf_str + s_o);
+        test("75null", "" + sf_str + sf_oNtS);
+        test("75true", "" + sf_str + s_bl);
+        test("753900", "" + sf_str + s_s);
+        test("75null", "" + sf_str + sf_oN);
+        test("7594000000", "" + sf_str + f_I);
+        test("75null", "" + sf_str + f_IN);
+        test("75true", "" + sf_str + sf_bl);
+        test("755500", "" + sf_str + sf_s);
+        test("75-2900", "" + sf_str + s_sM);
+        test("75-194313216", "" + sf_str + sf_l);
+        test("7512", "" + sf_str + s_strU1);
+        test("75C(87)", "" + sf_str + sf_o);
+        test("7591", "" + sf_str + s_strU2);
+        test("7521", "" + sf_str + f_strU1);
+        test("7518", "" + sf_str + f_strU2);
+        test("75null", "" + sf_str + f_iAN);
+        test("75null", "" + sf_str + s_oN);
+        test("75\u045180", "" + sf_str + s_strU);
+        test("75C", "" + sf_str + sf_c);
+        test("7575", "" + sf_str + sf_str);
+        test("75-43", "" + sf_str + s_bM);
+        test("7580", "" + sf_str + sf_b);
+        test("75null", "" + sf_str + s_IN);
+        test("75-52.0", "" + sf_str + s_fM);
+        test("7575000000", "" + sf_str + sf_i);
+        test("7544", "" + sf_str + f_b);
+        test("75-1705032704", "" + sf_str + sf_lM);
+        test("75null", "" + sf_str + f_oAN);
+        test("7583.0", "" + sf_str + f_d);
+        test("75I", "" + sf_str + f_c);
+        test("7594.0", "" + sf_str + f_f);
+        test("7512.0", "" + sf_str + sf_d);
+        test("75-99.0", "" + sf_str + f_dM);
+        test("7517.0", "" + sf_str + sf_f);
+        test("75-84.0", "" + sf_str + sf_dM);
+        test("7558000000", "" + sf_str + f_i);
+        test("75-55000000", "" + sf_str + f_iM);
+        test("751460392448", "" + sf_str + f_l);
+        test("75C(70)", "" + sf_str + f_o);
+        test("75\u04511", "" + sf_str + sf_strU);
+        test("758000", "" + sf_str + f_s);
+        test("7518", "" + sf_str + s_str);
+        test("75-1000000", "" + sf_str + s_iM);
+        test("751000000", "" + sf_str + sf_I);
+        test("75null", "" + sf_str + f_oNtS);
+        test("75false", "" + sf_str + f_bl);
+        test("75null", "" + sf_str + sf_iAN);
+        test("75-2000000", "" + sf_str + sf_iM);
+        test("75-820130816", "" + sf_str + f_lM);
+        test("75null", "" + sf_str + sf_oAN);
+        test("7525000000", "" + sf_str + s_I);
+        test("-43-96.0", "" + s_bM + s_dM);
+        test("-43null", "" + s_bM + s_oNtS);
+        test("-43\u045176", "" + s_bM + f_strU);
+        test("-4392", "" + s_bM + sf_strU2);
+        test("-4351", "" + s_bM + sf_strU1);
+        test("-43null", "" + s_bM + s_iAN);
+        test("-43-54", "" + s_bM + f_bM);
+        test("-43-87.0", "" + s_bM + f_fM);
+        test("-43null", "" + s_bM + s_oAN);
+        test("-4319", "" + s_bM + f_str);
+        test("-43-41", "" + s_bM + sf_bM);
+        test("-43null", "" + s_bM + sf_IN);
+        test("-43T", "" + s_bM + s_c);
+        test("-43-42.0", "" + s_bM + sf_fM);
+        test("-4325", "" + s_bM + s_b);
+        test("-43null", "" + s_bM + f_oN);
+        test("-43-1410065408", "" + s_bM + s_lM);
+        test("-438.0", "" + s_bM + s_d);
+        test("-4355.0", "" + s_bM + s_f);
+        test("-4397000000", "" + s_bM + s_i);
+        test("-43-9900", "" + s_bM + f_sM);
+        test("-43935228928", "" + s_bM + s_l);
+        test("-43-8400", "" + s_bM + sf_sM);
+        test("-43C(82)", "" + s_bM + s_o);
+        test("-43null", "" + s_bM + sf_oNtS);
+        test("-43true", "" + s_bM + s_bl);
+        test("-433900", "" + s_bM + s_s);
+        test("-43null", "" + s_bM + sf_oN);
+        test("-4394000000", "" + s_bM + f_I);
+        test("-43null", "" + s_bM + f_IN);
+        test("-43true", "" + s_bM + sf_bl);
+        test("-435500", "" + s_bM + sf_s);
+        test("-43-2900", "" + s_bM + s_sM);
+        test("-43-194313216", "" + s_bM + sf_l);
+        test("-4312", "" + s_bM + s_strU1);
+        test("-43C(87)", "" + s_bM + sf_o);
+        test("-4391", "" + s_bM + s_strU2);
+        test("-4321", "" + s_bM + f_strU1);
+        test("-4318", "" + s_bM + f_strU2);
+        test("-43null", "" + s_bM + f_iAN);
+        test("-43null", "" + s_bM + s_oN);
+        test("-43\u045180", "" + s_bM + s_strU);
+        test("-43C", "" + s_bM + sf_c);
+        test("-4375", "" + s_bM + sf_str);
+        test("-43-43", "" + s_bM + s_bM);
+        test("-4380", "" + s_bM + sf_b);
+        test("-43null", "" + s_bM + s_IN);
+        test("-43-52.0", "" + s_bM + s_fM);
+        test("-4375000000", "" + s_bM + sf_i);
+        test("-4344", "" + s_bM + f_b);
+        test("-43-1705032704", "" + s_bM + sf_lM);
+        test("-43null", "" + s_bM + f_oAN);
+        test("-4383.0", "" + s_bM + f_d);
+        test("-43I", "" + s_bM + f_c);
+        test("-4394.0", "" + s_bM + f_f);
+        test("-4312.0", "" + s_bM + sf_d);
+        test("-43-99.0", "" + s_bM + f_dM);
+        test("-4317.0", "" + s_bM + sf_f);
+        test("-43-84.0", "" + s_bM + sf_dM);
+        test("-4358000000", "" + s_bM + f_i);
+        test("-43-55000000", "" + s_bM + f_iM);
+        test("-431460392448", "" + s_bM + f_l);
+        test("-43C(70)", "" + s_bM + f_o);
+        test("-43\u04511", "" + s_bM + sf_strU);
+        test("-438000", "" + s_bM + f_s);
+        test("-4318", "" + s_bM + s_str);
+        test("-43-1000000", "" + s_bM + s_iM);
+        test("-431000000", "" + s_bM + sf_I);
+        test("-43null", "" + s_bM + f_oNtS);
+        test("-43false", "" + s_bM + f_bl);
+        test("-43null", "" + s_bM + sf_iAN);
+        test("-43-2000000", "" + s_bM + sf_iM);
+        test("-43-820130816", "" + s_bM + f_lM);
+        test("-43null", "" + s_bM + sf_oAN);
+        test("-4325000000", "" + s_bM + s_I);
+        test("80-96.0", "" + sf_b + s_dM);
+        test("80null", "" + sf_b + s_oNtS);
+        test("80\u045176", "" + sf_b + f_strU);
+        test("8092", "" + sf_b + sf_strU2);
+        test("8051", "" + sf_b + sf_strU1);
+        test("80null", "" + sf_b + s_iAN);
+        test("80-54", "" + sf_b + f_bM);
+        test("80-87.0", "" + sf_b + f_fM);
+        test("80null", "" + sf_b + s_oAN);
+        test("8019", "" + sf_b + f_str);
+        test("80-41", "" + sf_b + sf_bM);
+        test("80null", "" + sf_b + sf_IN);
+        test("80T", "" + sf_b + s_c);
+        test("80-42.0", "" + sf_b + sf_fM);
+        test("8025", "" + sf_b + s_b);
+        test("80null", "" + sf_b + f_oN);
+        test("80-1410065408", "" + sf_b + s_lM);
+        test("808.0", "" + sf_b + s_d);
+        test("8055.0", "" + sf_b + s_f);
+        test("8097000000", "" + sf_b + s_i);
+        test("80-9900", "" + sf_b + f_sM);
+        test("80935228928", "" + sf_b + s_l);
+        test("80-8400", "" + sf_b + sf_sM);
+        test("80C(82)", "" + sf_b + s_o);
+        test("80null", "" + sf_b + sf_oNtS);
+        test("80true", "" + sf_b + s_bl);
+        test("803900", "" + sf_b + s_s);
+        test("80null", "" + sf_b + sf_oN);
+        test("8094000000", "" + sf_b + f_I);
+        test("80null", "" + sf_b + f_IN);
+        test("80true", "" + sf_b + sf_bl);
+        test("805500", "" + sf_b + sf_s);
+        test("80-2900", "" + sf_b + s_sM);
+        test("80-194313216", "" + sf_b + sf_l);
+        test("8012", "" + sf_b + s_strU1);
+        test("80C(87)", "" + sf_b + sf_o);
+        test("8091", "" + sf_b + s_strU2);
+        test("8021", "" + sf_b + f_strU1);
+        test("8018", "" + sf_b + f_strU2);
+        test("80null", "" + sf_b + f_iAN);
+        test("80null", "" + sf_b + s_oN);
+        test("80\u045180", "" + sf_b + s_strU);
+        test("80C", "" + sf_b + sf_c);
+        test("8075", "" + sf_b + sf_str);
+        test("80-43", "" + sf_b + s_bM);
+        test("8080", "" + sf_b + sf_b);
+        test("80null", "" + sf_b + s_IN);
+        test("80-52.0", "" + sf_b + s_fM);
+        test("8075000000", "" + sf_b + sf_i);
+        test("8044", "" + sf_b + f_b);
+        test("80-1705032704", "" + sf_b + sf_lM);
+        test("80null", "" + sf_b + f_oAN);
+        test("8083.0", "" + sf_b + f_d);
+        test("80I", "" + sf_b + f_c);
+        test("8094.0", "" + sf_b + f_f);
+        test("8012.0", "" + sf_b + sf_d);
+        test("80-99.0", "" + sf_b + f_dM);
+        test("8017.0", "" + sf_b + sf_f);
+        test("80-84.0", "" + sf_b + sf_dM);
+        test("8058000000", "" + sf_b + f_i);
+        test("80-55000000", "" + sf_b + f_iM);
+        test("801460392448", "" + sf_b + f_l);
+        test("80C(70)", "" + sf_b + f_o);
+        test("80\u04511", "" + sf_b + sf_strU);
+        test("808000", "" + sf_b + f_s);
+        test("8018", "" + sf_b + s_str);
+        test("80-1000000", "" + sf_b + s_iM);
+        test("801000000", "" + sf_b + sf_I);
+        test("80null", "" + sf_b + f_oNtS);
+        test("80false", "" + sf_b + f_bl);
+        test("80null", "" + sf_b + sf_iAN);
+        test("80-2000000", "" + sf_b + sf_iM);
+        test("80-820130816", "" + sf_b + f_lM);
+        test("80null", "" + sf_b + sf_oAN);
+        test("8025000000", "" + sf_b + s_I);
+        test("null-96.0", "" + s_IN + s_dM);
+        test("nullnull", "" + s_IN + s_oNtS);
+        test("null\u045176", "" + s_IN + f_strU);
+        test("null92", "" + s_IN + sf_strU2);
+        test("null51", "" + s_IN + sf_strU1);
+        test("nullnull", "" + s_IN + s_iAN);
+        test("null-54", "" + s_IN + f_bM);
+        test("null-87.0", "" + s_IN + f_fM);
+        test("nullnull", "" + s_IN + s_oAN);
+        test("null19", "" + s_IN + f_str);
+        test("null-41", "" + s_IN + sf_bM);
+        test("nullnull", "" + s_IN + sf_IN);
+        test("nullT", "" + s_IN + s_c);
+        test("null-42.0", "" + s_IN + sf_fM);
+        test("null25", "" + s_IN + s_b);
+        test("nullnull", "" + s_IN + f_oN);
+        test("null-1410065408", "" + s_IN + s_lM);
+        test("null8.0", "" + s_IN + s_d);
+        test("null55.0", "" + s_IN + s_f);
+        test("null97000000", "" + s_IN + s_i);
+        test("null-9900", "" + s_IN + f_sM);
+        test("null935228928", "" + s_IN + s_l);
+        test("null-8400", "" + s_IN + sf_sM);
+        test("nullC(82)", "" + s_IN + s_o);
+        test("nullnull", "" + s_IN + sf_oNtS);
+        test("nulltrue", "" + s_IN + s_bl);
+        test("null3900", "" + s_IN + s_s);
+        test("nullnull", "" + s_IN + sf_oN);
+        test("null94000000", "" + s_IN + f_I);
+        test("nullnull", "" + s_IN + f_IN);
+        test("nulltrue", "" + s_IN + sf_bl);
+        test("null5500", "" + s_IN + sf_s);
+        test("null-2900", "" + s_IN + s_sM);
+        test("null-194313216", "" + s_IN + sf_l);
+        test("null12", "" + s_IN + s_strU1);
+        test("nullC(87)", "" + s_IN + sf_o);
+        test("null91", "" + s_IN + s_strU2);
+        test("null21", "" + s_IN + f_strU1);
+        test("null18", "" + s_IN + f_strU2);
+        test("nullnull", "" + s_IN + f_iAN);
+        test("nullnull", "" + s_IN + s_oN);
+        test("null\u045180", "" + s_IN + s_strU);
+        test("nullC", "" + s_IN + sf_c);
+        test("null75", "" + s_IN + sf_str);
+        test("null-43", "" + s_IN + s_bM);
+        test("null80", "" + s_IN + sf_b);
+        test("nullnull", "" + s_IN + s_IN);
+        test("null-52.0", "" + s_IN + s_fM);
+        test("null75000000", "" + s_IN + sf_i);
+        test("null44", "" + s_IN + f_b);
+        test("null-1705032704", "" + s_IN + sf_lM);
+        test("nullnull", "" + s_IN + f_oAN);
+        test("null83.0", "" + s_IN + f_d);
+        test("nullI", "" + s_IN + f_c);
+        test("null94.0", "" + s_IN + f_f);
+        test("null12.0", "" + s_IN + sf_d);
+        test("null-99.0", "" + s_IN + f_dM);
+        test("null17.0", "" + s_IN + sf_f);
+        test("null-84.0", "" + s_IN + sf_dM);
+        test("null58000000", "" + s_IN + f_i);
+        test("null-55000000", "" + s_IN + f_iM);
+        test("null1460392448", "" + s_IN + f_l);
+        test("nullC(70)", "" + s_IN + f_o);
+        test("null\u04511", "" + s_IN + sf_strU);
+        test("null8000", "" + s_IN + f_s);
+        test("null18", "" + s_IN + s_str);
+        test("null-1000000", "" + s_IN + s_iM);
+        test("null1000000", "" + s_IN + sf_I);
+        test("nullnull", "" + s_IN + f_oNtS);
+        test("nullfalse", "" + s_IN + f_bl);
+        test("nullnull", "" + s_IN + sf_iAN);
+        test("null-2000000", "" + s_IN + sf_iM);
+        test("null-820130816", "" + s_IN + f_lM);
+        test("nullnull", "" + s_IN + sf_oAN);
+        test("null25000000", "" + s_IN + s_I);
+        test("-52.0-96.0", "" + s_fM + s_dM);
+        test("-52.0null", "" + s_fM + s_oNtS);
+        test("-52.0\u045176", "" + s_fM + f_strU);
+        test("-52.092", "" + s_fM + sf_strU2);
+        test("-52.051", "" + s_fM + sf_strU1);
+        test("-52.0null", "" + s_fM + s_iAN);
+        test("-52.0-54", "" + s_fM + f_bM);
+        test("-52.0-87.0", "" + s_fM + f_fM);
+        test("-52.0null", "" + s_fM + s_oAN);
+        test("-52.019", "" + s_fM + f_str);
+        test("-52.0-41", "" + s_fM + sf_bM);
+        test("-52.0null", "" + s_fM + sf_IN);
+        test("-52.0T", "" + s_fM + s_c);
+        test("-52.0-42.0", "" + s_fM + sf_fM);
+        test("-52.025", "" + s_fM + s_b);
+        test("-52.0null", "" + s_fM + f_oN);
+        test("-52.0-1410065408", "" + s_fM + s_lM);
+        test("-52.08.0", "" + s_fM + s_d);
+        test("-52.055.0", "" + s_fM + s_f);
+        test("-52.097000000", "" + s_fM + s_i);
+        test("-52.0-9900", "" + s_fM + f_sM);
+        test("-52.0935228928", "" + s_fM + s_l);
+        test("-52.0-8400", "" + s_fM + sf_sM);
+        test("-52.0C(82)", "" + s_fM + s_o);
+        test("-52.0null", "" + s_fM + sf_oNtS);
+        test("-52.0true", "" + s_fM + s_bl);
+        test("-52.03900", "" + s_fM + s_s);
+        test("-52.0null", "" + s_fM + sf_oN);
+        test("-52.094000000", "" + s_fM + f_I);
+        test("-52.0null", "" + s_fM + f_IN);
+        test("-52.0true", "" + s_fM + sf_bl);
+        test("-52.05500", "" + s_fM + sf_s);
+        test("-52.0-2900", "" + s_fM + s_sM);
+        test("-52.0-194313216", "" + s_fM + sf_l);
+        test("-52.012", "" + s_fM + s_strU1);
+        test("-52.0C(87)", "" + s_fM + sf_o);
+        test("-52.091", "" + s_fM + s_strU2);
+        test("-52.021", "" + s_fM + f_strU1);
+        test("-52.018", "" + s_fM + f_strU2);
+        test("-52.0null", "" + s_fM + f_iAN);
+        test("-52.0null", "" + s_fM + s_oN);
+        test("-52.0\u045180", "" + s_fM + s_strU);
+        test("-52.0C", "" + s_fM + sf_c);
+        test("-52.075", "" + s_fM + sf_str);
+        test("-52.0-43", "" + s_fM + s_bM);
+        test("-52.080", "" + s_fM + sf_b);
+        test("-52.0null", "" + s_fM + s_IN);
+        test("-52.0-52.0", "" + s_fM + s_fM);
+        test("-52.075000000", "" + s_fM + sf_i);
+        test("-52.044", "" + s_fM + f_b);
+        test("-52.0-1705032704", "" + s_fM + sf_lM);
+        test("-52.0null", "" + s_fM + f_oAN);
+        test("-52.083.0", "" + s_fM + f_d);
+        test("-52.0I", "" + s_fM + f_c);
+        test("-52.094.0", "" + s_fM + f_f);
+        test("-52.012.0", "" + s_fM + sf_d);
+        test("-52.0-99.0", "" + s_fM + f_dM);
+        test("-52.017.0", "" + s_fM + sf_f);
+        test("-52.0-84.0", "" + s_fM + sf_dM);
+        test("-52.058000000", "" + s_fM + f_i);
+        test("-52.0-55000000", "" + s_fM + f_iM);
+        test("-52.01460392448", "" + s_fM + f_l);
+        test("-52.0C(70)", "" + s_fM + f_o);
+        test("-52.0\u04511", "" + s_fM + sf_strU);
+        test("-52.08000", "" + s_fM + f_s);
+        test("-52.018", "" + s_fM + s_str);
+        test("-52.0-1000000", "" + s_fM + s_iM);
+        test("-52.01000000", "" + s_fM + sf_I);
+        test("-52.0null", "" + s_fM + f_oNtS);
+        test("-52.0false", "" + s_fM + f_bl);
+        test("-52.0null", "" + s_fM + sf_iAN);
+        test("-52.0-2000000", "" + s_fM + sf_iM);
+        test("-52.0-820130816", "" + s_fM + f_lM);
+        test("-52.0null", "" + s_fM + sf_oAN);
+        test("-52.025000000", "" + s_fM + s_I);
+        test("75000000-96.0", "" + sf_i + s_dM);
+        test("75000000null", "" + sf_i + s_oNtS);
+        test("75000000\u045176", "" + sf_i + f_strU);
+        test("7500000092", "" + sf_i + sf_strU2);
+        test("7500000051", "" + sf_i + sf_strU1);
+        test("75000000null", "" + sf_i + s_iAN);
+        test("75000000-54", "" + sf_i + f_bM);
+        test("75000000-87.0", "" + sf_i + f_fM);
+        test("75000000null", "" + sf_i + s_oAN);
+        test("7500000019", "" + sf_i + f_str);
+        test("75000000-41", "" + sf_i + sf_bM);
+        test("75000000null", "" + sf_i + sf_IN);
+        test("75000000T", "" + sf_i + s_c);
+        test("75000000-42.0", "" + sf_i + sf_fM);
+        test("7500000025", "" + sf_i + s_b);
+        test("75000000null", "" + sf_i + f_oN);
+        test("75000000-1410065408", "" + sf_i + s_lM);
+        test("750000008.0", "" + sf_i + s_d);
+        test("7500000055.0", "" + sf_i + s_f);
+        test("7500000097000000", "" + sf_i + s_i);
+        test("75000000-9900", "" + sf_i + f_sM);
+        test("75000000935228928", "" + sf_i + s_l);
+        test("75000000-8400", "" + sf_i + sf_sM);
+        test("75000000C(82)", "" + sf_i + s_o);
+        test("75000000null", "" + sf_i + sf_oNtS);
+        test("75000000true", "" + sf_i + s_bl);
+        test("750000003900", "" + sf_i + s_s);
+        test("75000000null", "" + sf_i + sf_oN);
+        test("7500000094000000", "" + sf_i + f_I);
+        test("75000000null", "" + sf_i + f_IN);
+        test("75000000true", "" + sf_i + sf_bl);
+        test("750000005500", "" + sf_i + sf_s);
+        test("75000000-2900", "" + sf_i + s_sM);
+        test("75000000-194313216", "" + sf_i + sf_l);
+        test("7500000012", "" + sf_i + s_strU1);
+        test("75000000C(87)", "" + sf_i + sf_o);
+        test("7500000091", "" + sf_i + s_strU2);
+        test("7500000021", "" + sf_i + f_strU1);
+        test("7500000018", "" + sf_i + f_strU2);
+        test("75000000null", "" + sf_i + f_iAN);
+        test("75000000null", "" + sf_i + s_oN);
+        test("75000000\u045180", "" + sf_i + s_strU);
+        test("75000000C", "" + sf_i + sf_c);
+        test("7500000075", "" + sf_i + sf_str);
+        test("75000000-43", "" + sf_i + s_bM);
+        test("7500000080", "" + sf_i + sf_b);
+        test("75000000null", "" + sf_i + s_IN);
+        test("75000000-52.0", "" + sf_i + s_fM);
+        test("7500000075000000", "" + sf_i + sf_i);
+        test("7500000044", "" + sf_i + f_b);
+        test("75000000-1705032704", "" + sf_i + sf_lM);
+        test("75000000null", "" + sf_i + f_oAN);
+        test("7500000083.0", "" + sf_i + f_d);
+        test("75000000I", "" + sf_i + f_c);
+        test("7500000094.0", "" + sf_i + f_f);
+        test("7500000012.0", "" + sf_i + sf_d);
+        test("75000000-99.0", "" + sf_i + f_dM);
+        test("7500000017.0", "" + sf_i + sf_f);
+        test("75000000-84.0", "" + sf_i + sf_dM);
+        test("7500000058000000", "" + sf_i + f_i);
+        test("75000000-55000000", "" + sf_i + f_iM);
+        test("750000001460392448", "" + sf_i + f_l);
+        test("75000000C(70)", "" + sf_i + f_o);
+        test("75000000\u04511", "" + sf_i + sf_strU);
+        test("750000008000", "" + sf_i + f_s);
+        test("7500000018", "" + sf_i + s_str);
+        test("75000000-1000000", "" + sf_i + s_iM);
+        test("750000001000000", "" + sf_i + sf_I);
+        test("75000000null", "" + sf_i + f_oNtS);
+        test("75000000false", "" + sf_i + f_bl);
+        test("75000000null", "" + sf_i + sf_iAN);
+        test("75000000-2000000", "" + sf_i + sf_iM);
+        test("75000000-820130816", "" + sf_i + f_lM);
+        test("75000000null", "" + sf_i + sf_oAN);
+        test("7500000025000000", "" + sf_i + s_I);
+        test("44-96.0", "" + f_b + s_dM);
+        test("44null", "" + f_b + s_oNtS);
+        test("44\u045176", "" + f_b + f_strU);
+        test("4492", "" + f_b + sf_strU2);
+        test("4451", "" + f_b + sf_strU1);
+        test("44null", "" + f_b + s_iAN);
+        test("44-54", "" + f_b + f_bM);
+        test("44-87.0", "" + f_b + f_fM);
+        test("44null", "" + f_b + s_oAN);
+        test("4419", "" + f_b + f_str);
+        test("44-41", "" + f_b + sf_bM);
+        test("44null", "" + f_b + sf_IN);
+        test("44T", "" + f_b + s_c);
+        test("44-42.0", "" + f_b + sf_fM);
+        test("4425", "" + f_b + s_b);
+        test("44null", "" + f_b + f_oN);
+        test("44-1410065408", "" + f_b + s_lM);
+        test("448.0", "" + f_b + s_d);
+        test("4455.0", "" + f_b + s_f);
+        test("4497000000", "" + f_b + s_i);
+        test("44-9900", "" + f_b + f_sM);
+        test("44935228928", "" + f_b + s_l);
+        test("44-8400", "" + f_b + sf_sM);
+        test("44C(82)", "" + f_b + s_o);
+        test("44null", "" + f_b + sf_oNtS);
+        test("44true", "" + f_b + s_bl);
+        test("443900", "" + f_b + s_s);
+        test("44null", "" + f_b + sf_oN);
+        test("4494000000", "" + f_b + f_I);
+        test("44null", "" + f_b + f_IN);
+        test("44true", "" + f_b + sf_bl);
+        test("445500", "" + f_b + sf_s);
+        test("44-2900", "" + f_b + s_sM);
+        test("44-194313216", "" + f_b + sf_l);
+        test("4412", "" + f_b + s_strU1);
+        test("44C(87)", "" + f_b + sf_o);
+        test("4491", "" + f_b + s_strU2);
+        test("4421", "" + f_b + f_strU1);
+        test("4418", "" + f_b + f_strU2);
+        test("44null", "" + f_b + f_iAN);
+        test("44null", "" + f_b + s_oN);
+        test("44\u045180", "" + f_b + s_strU);
+        test("44C", "" + f_b + sf_c);
+        test("4475", "" + f_b + sf_str);
+        test("44-43", "" + f_b + s_bM);
+        test("4480", "" + f_b + sf_b);
+        test("44null", "" + f_b + s_IN);
+        test("44-52.0", "" + f_b + s_fM);
+        test("4475000000", "" + f_b + sf_i);
+        test("4444", "" + f_b + f_b);
+        test("44-1705032704", "" + f_b + sf_lM);
+        test("44null", "" + f_b + f_oAN);
+        test("4483.0", "" + f_b + f_d);
+        test("44I", "" + f_b + f_c);
+        test("4494.0", "" + f_b + f_f);
+        test("4412.0", "" + f_b + sf_d);
+        test("44-99.0", "" + f_b + f_dM);
+        test("4417.0", "" + f_b + sf_f);
+        test("44-84.0", "" + f_b + sf_dM);
+        test("4458000000", "" + f_b + f_i);
+        test("44-55000000", "" + f_b + f_iM);
+        test("441460392448", "" + f_b + f_l);
+        test("44C(70)", "" + f_b + f_o);
+        test("44\u04511", "" + f_b + sf_strU);
+        test("448000", "" + f_b + f_s);
+        test("4418", "" + f_b + s_str);
+        test("44-1000000", "" + f_b + s_iM);
+        test("441000000", "" + f_b + sf_I);
+        test("44null", "" + f_b + f_oNtS);
+        test("44false", "" + f_b + f_bl);
+        test("44null", "" + f_b + sf_iAN);
+        test("44-2000000", "" + f_b + sf_iM);
+        test("44-820130816", "" + f_b + f_lM);
+        test("44null", "" + f_b + sf_oAN);
+        test("4425000000", "" + f_b + s_I);
+        test("-1705032704-96.0", "" + sf_lM + s_dM);
+        test("-1705032704null", "" + sf_lM + s_oNtS);
+        test("-1705032704\u045176", "" + sf_lM + f_strU);
+        test("-170503270492", "" + sf_lM + sf_strU2);
+        test("-170503270451", "" + sf_lM + sf_strU1);
+        test("-1705032704null", "" + sf_lM + s_iAN);
+        test("-1705032704-54", "" + sf_lM + f_bM);
+        test("-1705032704-87.0", "" + sf_lM + f_fM);
+        test("-1705032704null", "" + sf_lM + s_oAN);
+        test("-170503270419", "" + sf_lM + f_str);
+        test("-1705032704-41", "" + sf_lM + sf_bM);
+        test("-1705032704null", "" + sf_lM + sf_IN);
+        test("-1705032704T", "" + sf_lM + s_c);
+        test("-1705032704-42.0", "" + sf_lM + sf_fM);
+        test("-170503270425", "" + sf_lM + s_b);
+        test("-1705032704null", "" + sf_lM + f_oN);
+        test("-1705032704-1410065408", "" + sf_lM + s_lM);
+        test("-17050327048.0", "" + sf_lM + s_d);
+        test("-170503270455.0", "" + sf_lM + s_f);
+        test("-170503270497000000", "" + sf_lM + s_i);
+        test("-1705032704-9900", "" + sf_lM + f_sM);
+        test("-1705032704935228928", "" + sf_lM + s_l);
+        test("-1705032704-8400", "" + sf_lM + sf_sM);
+        test("-1705032704C(82)", "" + sf_lM + s_o);
+        test("-1705032704null", "" + sf_lM + sf_oNtS);
+        test("-1705032704true", "" + sf_lM + s_bl);
+        test("-17050327043900", "" + sf_lM + s_s);
+        test("-1705032704null", "" + sf_lM + sf_oN);
+        test("-170503270494000000", "" + sf_lM + f_I);
+        test("-1705032704null", "" + sf_lM + f_IN);
+        test("-1705032704true", "" + sf_lM + sf_bl);
+        test("-17050327045500", "" + sf_lM + sf_s);
+        test("-1705032704-2900", "" + sf_lM + s_sM);
+        test("-1705032704-194313216", "" + sf_lM + sf_l);
+        test("-170503270412", "" + sf_lM + s_strU1);
+        test("-1705032704C(87)", "" + sf_lM + sf_o);
+        test("-170503270491", "" + sf_lM + s_strU2);
+        test("-170503270421", "" + sf_lM + f_strU1);
+        test("-170503270418", "" + sf_lM + f_strU2);
+        test("-1705032704null", "" + sf_lM + f_iAN);
+        test("-1705032704null", "" + sf_lM + s_oN);
+        test("-1705032704\u045180", "" + sf_lM + s_strU);
+        test("-1705032704C", "" + sf_lM + sf_c);
+        test("-170503270475", "" + sf_lM + sf_str);
+        test("-1705032704-43", "" + sf_lM + s_bM);
+        test("-170503270480", "" + sf_lM + sf_b);
+        test("-1705032704null", "" + sf_lM + s_IN);
+        test("-1705032704-52.0", "" + sf_lM + s_fM);
+        test("-170503270475000000", "" + sf_lM + sf_i);
+        test("-170503270444", "" + sf_lM + f_b);
+        test("-1705032704-1705032704", "" + sf_lM + sf_lM);
+        test("-1705032704null", "" + sf_lM + f_oAN);
+        test("-170503270483.0", "" + sf_lM + f_d);
+        test("-1705032704I", "" + sf_lM + f_c);
+        test("-170503270494.0", "" + sf_lM + f_f);
+        test("-170503270412.0", "" + sf_lM + sf_d);
+        test("-1705032704-99.0", "" + sf_lM + f_dM);
+        test("-170503270417.0", "" + sf_lM + sf_f);
+        test("-1705032704-84.0", "" + sf_lM + sf_dM);
+        test("-170503270458000000", "" + sf_lM + f_i);
+        test("-1705032704-55000000", "" + sf_lM + f_iM);
+        test("-17050327041460392448", "" + sf_lM + f_l);
+        test("-1705032704C(70)", "" + sf_lM + f_o);
+        test("-1705032704\u04511", "" + sf_lM + sf_strU);
+        test("-17050327048000", "" + sf_lM + f_s);
+        test("-170503270418", "" + sf_lM + s_str);
+        test("-1705032704-1000000", "" + sf_lM + s_iM);
+        test("-17050327041000000", "" + sf_lM + sf_I);
+        test("-1705032704null", "" + sf_lM + f_oNtS);
+        test("-1705032704false", "" + sf_lM + f_bl);
+        test("-1705032704null", "" + sf_lM + sf_iAN);
+        test("-1705032704-2000000", "" + sf_lM + sf_iM);
+        test("-1705032704-820130816", "" + sf_lM + f_lM);
+        test("-1705032704null", "" + sf_lM + sf_oAN);
+        test("-170503270425000000", "" + sf_lM + s_I);
+        test("null-96.0", "" + f_oAN + s_dM);
+        test("nullnull", "" + f_oAN + s_oNtS);
+        test("null\u045176", "" + f_oAN + f_strU);
+        test("null92", "" + f_oAN + sf_strU2);
+        test("null51", "" + f_oAN + sf_strU1);
+        test("nullnull", "" + f_oAN + s_iAN);
+        test("null-54", "" + f_oAN + f_bM);
+        test("null-87.0", "" + f_oAN + f_fM);
+        test("nullnull", "" + f_oAN + s_oAN);
+        test("null19", "" + f_oAN + f_str);
+        test("null-41", "" + f_oAN + sf_bM);
+        test("nullnull", "" + f_oAN + sf_IN);
+        test("nullT", "" + f_oAN + s_c);
+        test("null-42.0", "" + f_oAN + sf_fM);
+        test("null25", "" + f_oAN + s_b);
+        test("nullnull", "" + f_oAN + f_oN);
+        test("null-1410065408", "" + f_oAN + s_lM);
+        test("null8.0", "" + f_oAN + s_d);
+        test("null55.0", "" + f_oAN + s_f);
+        test("null97000000", "" + f_oAN + s_i);
+        test("null-9900", "" + f_oAN + f_sM);
+        test("null935228928", "" + f_oAN + s_l);
+        test("null-8400", "" + f_oAN + sf_sM);
+        test("nullC(82)", "" + f_oAN + s_o);
+        test("nullnull", "" + f_oAN + sf_oNtS);
+        test("nulltrue", "" + f_oAN + s_bl);
+        test("null3900", "" + f_oAN + s_s);
+        test("nullnull", "" + f_oAN + sf_oN);
+        test("null94000000", "" + f_oAN + f_I);
+        test("nullnull", "" + f_oAN + f_IN);
+        test("nulltrue", "" + f_oAN + sf_bl);
+        test("null5500", "" + f_oAN + sf_s);
+        test("null-2900", "" + f_oAN + s_sM);
+        test("null-194313216", "" + f_oAN + sf_l);
+        test("null12", "" + f_oAN + s_strU1);
+        test("nullC(87)", "" + f_oAN + sf_o);
+        test("null91", "" + f_oAN + s_strU2);
+        test("null21", "" + f_oAN + f_strU1);
+        test("null18", "" + f_oAN + f_strU2);
+        test("nullnull", "" + f_oAN + f_iAN);
+        test("nullnull", "" + f_oAN + s_oN);
+        test("null\u045180", "" + f_oAN + s_strU);
+        test("nullC", "" + f_oAN + sf_c);
+        test("null75", "" + f_oAN + sf_str);
+        test("null-43", "" + f_oAN + s_bM);
+        test("null80", "" + f_oAN + sf_b);
+        test("nullnull", "" + f_oAN + s_IN);
+        test("null-52.0", "" + f_oAN + s_fM);
+        test("null75000000", "" + f_oAN + sf_i);
+        test("null44", "" + f_oAN + f_b);
+        test("null-1705032704", "" + f_oAN + sf_lM);
+        test("nullnull", "" + f_oAN + f_oAN);
+        test("null83.0", "" + f_oAN + f_d);
+        test("nullI", "" + f_oAN + f_c);
+        test("null94.0", "" + f_oAN + f_f);
+        test("null12.0", "" + f_oAN + sf_d);
+        test("null-99.0", "" + f_oAN + f_dM);
+        test("null17.0", "" + f_oAN + sf_f);
+        test("null-84.0", "" + f_oAN + sf_dM);
+        test("null58000000", "" + f_oAN + f_i);
+        test("null-55000000", "" + f_oAN + f_iM);
+        test("null1460392448", "" + f_oAN + f_l);
+        test("nullC(70)", "" + f_oAN + f_o);
+        test("null\u04511", "" + f_oAN + sf_strU);
+        test("null8000", "" + f_oAN + f_s);
+        test("null18", "" + f_oAN + s_str);
+        test("null-1000000", "" + f_oAN + s_iM);
+        test("null1000000", "" + f_oAN + sf_I);
+        test("nullnull", "" + f_oAN + f_oNtS);
+        test("nullfalse", "" + f_oAN + f_bl);
+        test("nullnull", "" + f_oAN + sf_iAN);
+        test("null-2000000", "" + f_oAN + sf_iM);
+        test("null-820130816", "" + f_oAN + f_lM);
+        test("nullnull", "" + f_oAN + sf_oAN);
+        test("null25000000", "" + f_oAN + s_I);
+        test("83.0-96.0", "" + f_d + s_dM);
+        test("83.0null", "" + f_d + s_oNtS);
+        test("83.0\u045176", "" + f_d + f_strU);
+        test("83.092", "" + f_d + sf_strU2);
+        test("83.051", "" + f_d + sf_strU1);
+        test("83.0null", "" + f_d + s_iAN);
+        test("83.0-54", "" + f_d + f_bM);
+        test("83.0-87.0", "" + f_d + f_fM);
+        test("83.0null", "" + f_d + s_oAN);
+        test("83.019", "" + f_d + f_str);
+        test("83.0-41", "" + f_d + sf_bM);
+        test("83.0null", "" + f_d + sf_IN);
+        test("83.0T", "" + f_d + s_c);
+        test("83.0-42.0", "" + f_d + sf_fM);
+        test("83.025", "" + f_d + s_b);
+        test("83.0null", "" + f_d + f_oN);
+        test("83.0-1410065408", "" + f_d + s_lM);
+        test("83.08.0", "" + f_d + s_d);
+        test("83.055.0", "" + f_d + s_f);
+        test("83.097000000", "" + f_d + s_i);
+        test("83.0-9900", "" + f_d + f_sM);
+        test("83.0935228928", "" + f_d + s_l);
+        test("83.0-8400", "" + f_d + sf_sM);
+        test("83.0C(82)", "" + f_d + s_o);
+        test("83.0null", "" + f_d + sf_oNtS);
+    }
+
+    public void run4() {
+        test("83.0true", "" + f_d + s_bl);
+        test("83.03900", "" + f_d + s_s);
+        test("83.0null", "" + f_d + sf_oN);
+        test("83.094000000", "" + f_d + f_I);
+        test("83.0null", "" + f_d + f_IN);
+        test("83.0true", "" + f_d + sf_bl);
+        test("83.05500", "" + f_d + sf_s);
+        test("83.0-2900", "" + f_d + s_sM);
+        test("83.0-194313216", "" + f_d + sf_l);
+        test("83.012", "" + f_d + s_strU1);
+        test("83.0C(87)", "" + f_d + sf_o);
+        test("83.091", "" + f_d + s_strU2);
+        test("83.021", "" + f_d + f_strU1);
+        test("83.018", "" + f_d + f_strU2);
+        test("83.0null", "" + f_d + f_iAN);
+        test("83.0null", "" + f_d + s_oN);
+        test("83.0\u045180", "" + f_d + s_strU);
+        test("83.0C", "" + f_d + sf_c);
+        test("83.075", "" + f_d + sf_str);
+        test("83.0-43", "" + f_d + s_bM);
+        test("83.080", "" + f_d + sf_b);
+        test("83.0null", "" + f_d + s_IN);
+        test("83.0-52.0", "" + f_d + s_fM);
+        test("83.075000000", "" + f_d + sf_i);
+        test("83.044", "" + f_d + f_b);
+        test("83.0-1705032704", "" + f_d + sf_lM);
+        test("83.0null", "" + f_d + f_oAN);
+        test("83.083.0", "" + f_d + f_d);
+        test("83.0I", "" + f_d + f_c);
+        test("83.094.0", "" + f_d + f_f);
+        test("83.012.0", "" + f_d + sf_d);
+        test("83.0-99.0", "" + f_d + f_dM);
+        test("83.017.0", "" + f_d + sf_f);
+        test("83.0-84.0", "" + f_d + sf_dM);
+        test("83.058000000", "" + f_d + f_i);
+        test("83.0-55000000", "" + f_d + f_iM);
+        test("83.01460392448", "" + f_d + f_l);
+        test("83.0C(70)", "" + f_d + f_o);
+        test("83.0\u04511", "" + f_d + sf_strU);
+        test("83.08000", "" + f_d + f_s);
+        test("83.018", "" + f_d + s_str);
+        test("83.0-1000000", "" + f_d + s_iM);
+        test("83.01000000", "" + f_d + sf_I);
+        test("83.0null", "" + f_d + f_oNtS);
+        test("83.0false", "" + f_d + f_bl);
+        test("83.0null", "" + f_d + sf_iAN);
+        test("83.0-2000000", "" + f_d + sf_iM);
+        test("83.0-820130816", "" + f_d + f_lM);
+        test("83.0null", "" + f_d + sf_oAN);
+        test("83.025000000", "" + f_d + s_I);
+        test("I-96.0", "" + f_c + s_dM);
+        test("Inull", "" + f_c + s_oNtS);
+        test("I\u045176", "" + f_c + f_strU);
+        test("I92", "" + f_c + sf_strU2);
+        test("I51", "" + f_c + sf_strU1);
+        test("Inull", "" + f_c + s_iAN);
+        test("I-54", "" + f_c + f_bM);
+        test("I-87.0", "" + f_c + f_fM);
+        test("Inull", "" + f_c + s_oAN);
+        test("I19", "" + f_c + f_str);
+        test("I-41", "" + f_c + sf_bM);
+        test("Inull", "" + f_c + sf_IN);
+        test("IT", "" + f_c + s_c);
+        test("I-42.0", "" + f_c + sf_fM);
+        test("I25", "" + f_c + s_b);
+        test("Inull", "" + f_c + f_oN);
+        test("I-1410065408", "" + f_c + s_lM);
+        test("I8.0", "" + f_c + s_d);
+        test("I55.0", "" + f_c + s_f);
+        test("I97000000", "" + f_c + s_i);
+        test("I-9900", "" + f_c + f_sM);
+        test("I935228928", "" + f_c + s_l);
+        test("I-8400", "" + f_c + sf_sM);
+        test("IC(82)", "" + f_c + s_o);
+        test("Inull", "" + f_c + sf_oNtS);
+        test("Itrue", "" + f_c + s_bl);
+        test("I3900", "" + f_c + s_s);
+        test("Inull", "" + f_c + sf_oN);
+        test("I94000000", "" + f_c + f_I);
+        test("Inull", "" + f_c + f_IN);
+        test("Itrue", "" + f_c + sf_bl);
+        test("I5500", "" + f_c + sf_s);
+        test("I-2900", "" + f_c + s_sM);
+        test("I-194313216", "" + f_c + sf_l);
+        test("I12", "" + f_c + s_strU1);
+        test("IC(87)", "" + f_c + sf_o);
+        test("I91", "" + f_c + s_strU2);
+        test("I21", "" + f_c + f_strU1);
+        test("I18", "" + f_c + f_strU2);
+        test("Inull", "" + f_c + f_iAN);
+        test("Inull", "" + f_c + s_oN);
+        test("I\u045180", "" + f_c + s_strU);
+        test("IC", "" + f_c + sf_c);
+        test("I75", "" + f_c + sf_str);
+        test("I-43", "" + f_c + s_bM);
+        test("I80", "" + f_c + sf_b);
+        test("Inull", "" + f_c + s_IN);
+        test("I-52.0", "" + f_c + s_fM);
+        test("I75000000", "" + f_c + sf_i);
+        test("I44", "" + f_c + f_b);
+        test("I-1705032704", "" + f_c + sf_lM);
+        test("Inull", "" + f_c + f_oAN);
+        test("I83.0", "" + f_c + f_d);
+        test("II", "" + f_c + f_c);
+        test("I94.0", "" + f_c + f_f);
+        test("I12.0", "" + f_c + sf_d);
+        test("I-99.0", "" + f_c + f_dM);
+        test("I17.0", "" + f_c + sf_f);
+        test("I-84.0", "" + f_c + sf_dM);
+        test("I58000000", "" + f_c + f_i);
+        test("I-55000000", "" + f_c + f_iM);
+        test("I1460392448", "" + f_c + f_l);
+        test("IC(70)", "" + f_c + f_o);
+        test("I\u04511", "" + f_c + sf_strU);
+        test("I8000", "" + f_c + f_s);
+        test("I18", "" + f_c + s_str);
+        test("I-1000000", "" + f_c + s_iM);
+        test("I1000000", "" + f_c + sf_I);
+        test("Inull", "" + f_c + f_oNtS);
+        test("Ifalse", "" + f_c + f_bl);
+        test("Inull", "" + f_c + sf_iAN);
+        test("I-2000000", "" + f_c + sf_iM);
+        test("I-820130816", "" + f_c + f_lM);
+        test("Inull", "" + f_c + sf_oAN);
+        test("I25000000", "" + f_c + s_I);
+        test("94.0-96.0", "" + f_f + s_dM);
+        test("94.0null", "" + f_f + s_oNtS);
+        test("94.0\u045176", "" + f_f + f_strU);
+        test("94.092", "" + f_f + sf_strU2);
+        test("94.051", "" + f_f + sf_strU1);
+        test("94.0null", "" + f_f + s_iAN);
+        test("94.0-54", "" + f_f + f_bM);
+        test("94.0-87.0", "" + f_f + f_fM);
+        test("94.0null", "" + f_f + s_oAN);
+        test("94.019", "" + f_f + f_str);
+        test("94.0-41", "" + f_f + sf_bM);
+        test("94.0null", "" + f_f + sf_IN);
+        test("94.0T", "" + f_f + s_c);
+        test("94.0-42.0", "" + f_f + sf_fM);
+        test("94.025", "" + f_f + s_b);
+        test("94.0null", "" + f_f + f_oN);
+        test("94.0-1410065408", "" + f_f + s_lM);
+        test("94.08.0", "" + f_f + s_d);
+        test("94.055.0", "" + f_f + s_f);
+        test("94.097000000", "" + f_f + s_i);
+        test("94.0-9900", "" + f_f + f_sM);
+        test("94.0935228928", "" + f_f + s_l);
+        test("94.0-8400", "" + f_f + sf_sM);
+        test("94.0C(82)", "" + f_f + s_o);
+        test("94.0null", "" + f_f + sf_oNtS);
+        test("94.0true", "" + f_f + s_bl);
+        test("94.03900", "" + f_f + s_s);
+        test("94.0null", "" + f_f + sf_oN);
+        test("94.094000000", "" + f_f + f_I);
+        test("94.0null", "" + f_f + f_IN);
+        test("94.0true", "" + f_f + sf_bl);
+        test("94.05500", "" + f_f + sf_s);
+        test("94.0-2900", "" + f_f + s_sM);
+        test("94.0-194313216", "" + f_f + sf_l);
+        test("94.012", "" + f_f + s_strU1);
+        test("94.0C(87)", "" + f_f + sf_o);
+        test("94.091", "" + f_f + s_strU2);
+        test("94.021", "" + f_f + f_strU1);
+        test("94.018", "" + f_f + f_strU2);
+        test("94.0null", "" + f_f + f_iAN);
+        test("94.0null", "" + f_f + s_oN);
+        test("94.0\u045180", "" + f_f + s_strU);
+        test("94.0C", "" + f_f + sf_c);
+        test("94.075", "" + f_f + sf_str);
+        test("94.0-43", "" + f_f + s_bM);
+        test("94.080", "" + f_f + sf_b);
+        test("94.0null", "" + f_f + s_IN);
+        test("94.0-52.0", "" + f_f + s_fM);
+        test("94.075000000", "" + f_f + sf_i);
+        test("94.044", "" + f_f + f_b);
+        test("94.0-1705032704", "" + f_f + sf_lM);
+        test("94.0null", "" + f_f + f_oAN);
+        test("94.083.0", "" + f_f + f_d);
+        test("94.0I", "" + f_f + f_c);
+        test("94.094.0", "" + f_f + f_f);
+        test("94.012.0", "" + f_f + sf_d);
+        test("94.0-99.0", "" + f_f + f_dM);
+        test("94.017.0", "" + f_f + sf_f);
+        test("94.0-84.0", "" + f_f + sf_dM);
+        test("94.058000000", "" + f_f + f_i);
+        test("94.0-55000000", "" + f_f + f_iM);
+        test("94.01460392448", "" + f_f + f_l);
+        test("94.0C(70)", "" + f_f + f_o);
+        test("94.0\u04511", "" + f_f + sf_strU);
+        test("94.08000", "" + f_f + f_s);
+        test("94.018", "" + f_f + s_str);
+        test("94.0-1000000", "" + f_f + s_iM);
+        test("94.01000000", "" + f_f + sf_I);
+        test("94.0null", "" + f_f + f_oNtS);
+        test("94.0false", "" + f_f + f_bl);
+        test("94.0null", "" + f_f + sf_iAN);
+        test("94.0-2000000", "" + f_f + sf_iM);
+        test("94.0-820130816", "" + f_f + f_lM);
+        test("94.0null", "" + f_f + sf_oAN);
+        test("94.025000000", "" + f_f + s_I);
+        test("12.0-96.0", "" + sf_d + s_dM);
+        test("12.0null", "" + sf_d + s_oNtS);
+        test("12.0\u045176", "" + sf_d + f_strU);
+        test("12.092", "" + sf_d + sf_strU2);
+        test("12.051", "" + sf_d + sf_strU1);
+        test("12.0null", "" + sf_d + s_iAN);
+        test("12.0-54", "" + sf_d + f_bM);
+        test("12.0-87.0", "" + sf_d + f_fM);
+        test("12.0null", "" + sf_d + s_oAN);
+        test("12.019", "" + sf_d + f_str);
+        test("12.0-41", "" + sf_d + sf_bM);
+        test("12.0null", "" + sf_d + sf_IN);
+        test("12.0T", "" + sf_d + s_c);
+        test("12.0-42.0", "" + sf_d + sf_fM);
+        test("12.025", "" + sf_d + s_b);
+        test("12.0null", "" + sf_d + f_oN);
+        test("12.0-1410065408", "" + sf_d + s_lM);
+        test("12.08.0", "" + sf_d + s_d);
+        test("12.055.0", "" + sf_d + s_f);
+        test("12.097000000", "" + sf_d + s_i);
+        test("12.0-9900", "" + sf_d + f_sM);
+        test("12.0935228928", "" + sf_d + s_l);
+        test("12.0-8400", "" + sf_d + sf_sM);
+        test("12.0C(82)", "" + sf_d + s_o);
+        test("12.0null", "" + sf_d + sf_oNtS);
+        test("12.0true", "" + sf_d + s_bl);
+        test("12.03900", "" + sf_d + s_s);
+        test("12.0null", "" + sf_d + sf_oN);
+        test("12.094000000", "" + sf_d + f_I);
+        test("12.0null", "" + sf_d + f_IN);
+        test("12.0true", "" + sf_d + sf_bl);
+        test("12.05500", "" + sf_d + sf_s);
+        test("12.0-2900", "" + sf_d + s_sM);
+        test("12.0-194313216", "" + sf_d + sf_l);
+        test("12.012", "" + sf_d + s_strU1);
+        test("12.0C(87)", "" + sf_d + sf_o);
+        test("12.091", "" + sf_d + s_strU2);
+        test("12.021", "" + sf_d + f_strU1);
+        test("12.018", "" + sf_d + f_strU2);
+        test("12.0null", "" + sf_d + f_iAN);
+        test("12.0null", "" + sf_d + s_oN);
+        test("12.0\u045180", "" + sf_d + s_strU);
+        test("12.0C", "" + sf_d + sf_c);
+        test("12.075", "" + sf_d + sf_str);
+        test("12.0-43", "" + sf_d + s_bM);
+        test("12.080", "" + sf_d + sf_b);
+        test("12.0null", "" + sf_d + s_IN);
+        test("12.0-52.0", "" + sf_d + s_fM);
+        test("12.075000000", "" + sf_d + sf_i);
+        test("12.044", "" + sf_d + f_b);
+        test("12.0-1705032704", "" + sf_d + sf_lM);
+        test("12.0null", "" + sf_d + f_oAN);
+        test("12.083.0", "" + sf_d + f_d);
+        test("12.0I", "" + sf_d + f_c);
+        test("12.094.0", "" + sf_d + f_f);
+        test("12.012.0", "" + sf_d + sf_d);
+        test("12.0-99.0", "" + sf_d + f_dM);
+        test("12.017.0", "" + sf_d + sf_f);
+        test("12.0-84.0", "" + sf_d + sf_dM);
+        test("12.058000000", "" + sf_d + f_i);
+        test("12.0-55000000", "" + sf_d + f_iM);
+        test("12.01460392448", "" + sf_d + f_l);
+        test("12.0C(70)", "" + sf_d + f_o);
+        test("12.0\u04511", "" + sf_d + sf_strU);
+        test("12.08000", "" + sf_d + f_s);
+        test("12.018", "" + sf_d + s_str);
+        test("12.0-1000000", "" + sf_d + s_iM);
+        test("12.01000000", "" + sf_d + sf_I);
+        test("12.0null", "" + sf_d + f_oNtS);
+        test("12.0false", "" + sf_d + f_bl);
+        test("12.0null", "" + sf_d + sf_iAN);
+        test("12.0-2000000", "" + sf_d + sf_iM);
+        test("12.0-820130816", "" + sf_d + f_lM);
+        test("12.0null", "" + sf_d + sf_oAN);
+        test("12.025000000", "" + sf_d + s_I);
+        test("-99.0-96.0", "" + f_dM + s_dM);
+        test("-99.0null", "" + f_dM + s_oNtS);
+        test("-99.0\u045176", "" + f_dM + f_strU);
+        test("-99.092", "" + f_dM + sf_strU2);
+        test("-99.051", "" + f_dM + sf_strU1);
+        test("-99.0null", "" + f_dM + s_iAN);
+        test("-99.0-54", "" + f_dM + f_bM);
+        test("-99.0-87.0", "" + f_dM + f_fM);
+        test("-99.0null", "" + f_dM + s_oAN);
+        test("-99.019", "" + f_dM + f_str);
+        test("-99.0-41", "" + f_dM + sf_bM);
+        test("-99.0null", "" + f_dM + sf_IN);
+        test("-99.0T", "" + f_dM + s_c);
+        test("-99.0-42.0", "" + f_dM + sf_fM);
+        test("-99.025", "" + f_dM + s_b);
+        test("-99.0null", "" + f_dM + f_oN);
+        test("-99.0-1410065408", "" + f_dM + s_lM);
+        test("-99.08.0", "" + f_dM + s_d);
+        test("-99.055.0", "" + f_dM + s_f);
+        test("-99.097000000", "" + f_dM + s_i);
+        test("-99.0-9900", "" + f_dM + f_sM);
+        test("-99.0935228928", "" + f_dM + s_l);
+        test("-99.0-8400", "" + f_dM + sf_sM);
+        test("-99.0C(82)", "" + f_dM + s_o);
+        test("-99.0null", "" + f_dM + sf_oNtS);
+        test("-99.0true", "" + f_dM + s_bl);
+        test("-99.03900", "" + f_dM + s_s);
+        test("-99.0null", "" + f_dM + sf_oN);
+        test("-99.094000000", "" + f_dM + f_I);
+        test("-99.0null", "" + f_dM + f_IN);
+        test("-99.0true", "" + f_dM + sf_bl);
+        test("-99.05500", "" + f_dM + sf_s);
+        test("-99.0-2900", "" + f_dM + s_sM);
+        test("-99.0-194313216", "" + f_dM + sf_l);
+        test("-99.012", "" + f_dM + s_strU1);
+        test("-99.0C(87)", "" + f_dM + sf_o);
+        test("-99.091", "" + f_dM + s_strU2);
+        test("-99.021", "" + f_dM + f_strU1);
+        test("-99.018", "" + f_dM + f_strU2);
+        test("-99.0null", "" + f_dM + f_iAN);
+        test("-99.0null", "" + f_dM + s_oN);
+        test("-99.0\u045180", "" + f_dM + s_strU);
+        test("-99.0C", "" + f_dM + sf_c);
+        test("-99.075", "" + f_dM + sf_str);
+        test("-99.0-43", "" + f_dM + s_bM);
+        test("-99.080", "" + f_dM + sf_b);
+        test("-99.0null", "" + f_dM + s_IN);
+        test("-99.0-52.0", "" + f_dM + s_fM);
+        test("-99.075000000", "" + f_dM + sf_i);
+        test("-99.044", "" + f_dM + f_b);
+        test("-99.0-1705032704", "" + f_dM + sf_lM);
+        test("-99.0null", "" + f_dM + f_oAN);
+        test("-99.083.0", "" + f_dM + f_d);
+        test("-99.0I", "" + f_dM + f_c);
+        test("-99.094.0", "" + f_dM + f_f);
+        test("-99.012.0", "" + f_dM + sf_d);
+        test("-99.0-99.0", "" + f_dM + f_dM);
+        test("-99.017.0", "" + f_dM + sf_f);
+        test("-99.0-84.0", "" + f_dM + sf_dM);
+        test("-99.058000000", "" + f_dM + f_i);
+        test("-99.0-55000000", "" + f_dM + f_iM);
+        test("-99.01460392448", "" + f_dM + f_l);
+        test("-99.0C(70)", "" + f_dM + f_o);
+        test("-99.0\u04511", "" + f_dM + sf_strU);
+        test("-99.08000", "" + f_dM + f_s);
+        test("-99.018", "" + f_dM + s_str);
+        test("-99.0-1000000", "" + f_dM + s_iM);
+        test("-99.01000000", "" + f_dM + sf_I);
+        test("-99.0null", "" + f_dM + f_oNtS);
+        test("-99.0false", "" + f_dM + f_bl);
+        test("-99.0null", "" + f_dM + sf_iAN);
+        test("-99.0-2000000", "" + f_dM + sf_iM);
+        test("-99.0-820130816", "" + f_dM + f_lM);
+        test("-99.0null", "" + f_dM + sf_oAN);
+        test("-99.025000000", "" + f_dM + s_I);
+        test("17.0-96.0", "" + sf_f + s_dM);
+        test("17.0null", "" + sf_f + s_oNtS);
+        test("17.0\u045176", "" + sf_f + f_strU);
+        test("17.092", "" + sf_f + sf_strU2);
+        test("17.051", "" + sf_f + sf_strU1);
+        test("17.0null", "" + sf_f + s_iAN);
+        test("17.0-54", "" + sf_f + f_bM);
+        test("17.0-87.0", "" + sf_f + f_fM);
+        test("17.0null", "" + sf_f + s_oAN);
+        test("17.019", "" + sf_f + f_str);
+        test("17.0-41", "" + sf_f + sf_bM);
+        test("17.0null", "" + sf_f + sf_IN);
+        test("17.0T", "" + sf_f + s_c);
+        test("17.0-42.0", "" + sf_f + sf_fM);
+        test("17.025", "" + sf_f + s_b);
+        test("17.0null", "" + sf_f + f_oN);
+        test("17.0-1410065408", "" + sf_f + s_lM);
+        test("17.08.0", "" + sf_f + s_d);
+        test("17.055.0", "" + sf_f + s_f);
+        test("17.097000000", "" + sf_f + s_i);
+        test("17.0-9900", "" + sf_f + f_sM);
+        test("17.0935228928", "" + sf_f + s_l);
+        test("17.0-8400", "" + sf_f + sf_sM);
+        test("17.0C(82)", "" + sf_f + s_o);
+        test("17.0null", "" + sf_f + sf_oNtS);
+        test("17.0true", "" + sf_f + s_bl);
+        test("17.03900", "" + sf_f + s_s);
+        test("17.0null", "" + sf_f + sf_oN);
+        test("17.094000000", "" + sf_f + f_I);
+        test("17.0null", "" + sf_f + f_IN);
+        test("17.0true", "" + sf_f + sf_bl);
+        test("17.05500", "" + sf_f + sf_s);
+        test("17.0-2900", "" + sf_f + s_sM);
+        test("17.0-194313216", "" + sf_f + sf_l);
+        test("17.012", "" + sf_f + s_strU1);
+        test("17.0C(87)", "" + sf_f + sf_o);
+        test("17.091", "" + sf_f + s_strU2);
+        test("17.021", "" + sf_f + f_strU1);
+        test("17.018", "" + sf_f + f_strU2);
+        test("17.0null", "" + sf_f + f_iAN);
+        test("17.0null", "" + sf_f + s_oN);
+        test("17.0\u045180", "" + sf_f + s_strU);
+        test("17.0C", "" + sf_f + sf_c);
+        test("17.075", "" + sf_f + sf_str);
+        test("17.0-43", "" + sf_f + s_bM);
+        test("17.080", "" + sf_f + sf_b);
+        test("17.0null", "" + sf_f + s_IN);
+        test("17.0-52.0", "" + sf_f + s_fM);
+        test("17.075000000", "" + sf_f + sf_i);
+        test("17.044", "" + sf_f + f_b);
+        test("17.0-1705032704", "" + sf_f + sf_lM);
+        test("17.0null", "" + sf_f + f_oAN);
+        test("17.083.0", "" + sf_f + f_d);
+        test("17.0I", "" + sf_f + f_c);
+        test("17.094.0", "" + sf_f + f_f);
+        test("17.012.0", "" + sf_f + sf_d);
+        test("17.0-99.0", "" + sf_f + f_dM);
+        test("17.017.0", "" + sf_f + sf_f);
+        test("17.0-84.0", "" + sf_f + sf_dM);
+        test("17.058000000", "" + sf_f + f_i);
+        test("17.0-55000000", "" + sf_f + f_iM);
+        test("17.01460392448", "" + sf_f + f_l);
+        test("17.0C(70)", "" + sf_f + f_o);
+        test("17.0\u04511", "" + sf_f + sf_strU);
+        test("17.08000", "" + sf_f + f_s);
+        test("17.018", "" + sf_f + s_str);
+        test("17.0-1000000", "" + sf_f + s_iM);
+        test("17.01000000", "" + sf_f + sf_I);
+        test("17.0null", "" + sf_f + f_oNtS);
+        test("17.0false", "" + sf_f + f_bl);
+        test("17.0null", "" + sf_f + sf_iAN);
+        test("17.0-2000000", "" + sf_f + sf_iM);
+        test("17.0-820130816", "" + sf_f + f_lM);
+        test("17.0null", "" + sf_f + sf_oAN);
+        test("17.025000000", "" + sf_f + s_I);
+        test("-84.0-96.0", "" + sf_dM + s_dM);
+        test("-84.0null", "" + sf_dM + s_oNtS);
+        test("-84.0\u045176", "" + sf_dM + f_strU);
+        test("-84.092", "" + sf_dM + sf_strU2);
+        test("-84.051", "" + sf_dM + sf_strU1);
+        test("-84.0null", "" + sf_dM + s_iAN);
+        test("-84.0-54", "" + sf_dM + f_bM);
+        test("-84.0-87.0", "" + sf_dM + f_fM);
+        test("-84.0null", "" + sf_dM + s_oAN);
+        test("-84.019", "" + sf_dM + f_str);
+        test("-84.0-41", "" + sf_dM + sf_bM);
+        test("-84.0null", "" + sf_dM + sf_IN);
+        test("-84.0T", "" + sf_dM + s_c);
+        test("-84.0-42.0", "" + sf_dM + sf_fM);
+        test("-84.025", "" + sf_dM + s_b);
+        test("-84.0null", "" + sf_dM + f_oN);
+        test("-84.0-1410065408", "" + sf_dM + s_lM);
+        test("-84.08.0", "" + sf_dM + s_d);
+        test("-84.055.0", "" + sf_dM + s_f);
+        test("-84.097000000", "" + sf_dM + s_i);
+        test("-84.0-9900", "" + sf_dM + f_sM);
+        test("-84.0935228928", "" + sf_dM + s_l);
+        test("-84.0-8400", "" + sf_dM + sf_sM);
+        test("-84.0C(82)", "" + sf_dM + s_o);
+        test("-84.0null", "" + sf_dM + sf_oNtS);
+        test("-84.0true", "" + sf_dM + s_bl);
+        test("-84.03900", "" + sf_dM + s_s);
+        test("-84.0null", "" + sf_dM + sf_oN);
+        test("-84.094000000", "" + sf_dM + f_I);
+        test("-84.0null", "" + sf_dM + f_IN);
+        test("-84.0true", "" + sf_dM + sf_bl);
+        test("-84.05500", "" + sf_dM + sf_s);
+        test("-84.0-2900", "" + sf_dM + s_sM);
+        test("-84.0-194313216", "" + sf_dM + sf_l);
+        test("-84.012", "" + sf_dM + s_strU1);
+        test("-84.0C(87)", "" + sf_dM + sf_o);
+        test("-84.091", "" + sf_dM + s_strU2);
+        test("-84.021", "" + sf_dM + f_strU1);
+        test("-84.018", "" + sf_dM + f_strU2);
+        test("-84.0null", "" + sf_dM + f_iAN);
+        test("-84.0null", "" + sf_dM + s_oN);
+        test("-84.0\u045180", "" + sf_dM + s_strU);
+        test("-84.0C", "" + sf_dM + sf_c);
+        test("-84.075", "" + sf_dM + sf_str);
+        test("-84.0-43", "" + sf_dM + s_bM);
+        test("-84.080", "" + sf_dM + sf_b);
+        test("-84.0null", "" + sf_dM + s_IN);
+        test("-84.0-52.0", "" + sf_dM + s_fM);
+        test("-84.075000000", "" + sf_dM + sf_i);
+        test("-84.044", "" + sf_dM + f_b);
+        test("-84.0-1705032704", "" + sf_dM + sf_lM);
+        test("-84.0null", "" + sf_dM + f_oAN);
+        test("-84.083.0", "" + sf_dM + f_d);
+        test("-84.0I", "" + sf_dM + f_c);
+        test("-84.094.0", "" + sf_dM + f_f);
+        test("-84.012.0", "" + sf_dM + sf_d);
+        test("-84.0-99.0", "" + sf_dM + f_dM);
+        test("-84.017.0", "" + sf_dM + sf_f);
+        test("-84.0-84.0", "" + sf_dM + sf_dM);
+        test("-84.058000000", "" + sf_dM + f_i);
+        test("-84.0-55000000", "" + sf_dM + f_iM);
+        test("-84.01460392448", "" + sf_dM + f_l);
+        test("-84.0C(70)", "" + sf_dM + f_o);
+        test("-84.0\u04511", "" + sf_dM + sf_strU);
+        test("-84.08000", "" + sf_dM + f_s);
+        test("-84.018", "" + sf_dM + s_str);
+        test("-84.0-1000000", "" + sf_dM + s_iM);
+        test("-84.01000000", "" + sf_dM + sf_I);
+        test("-84.0null", "" + sf_dM + f_oNtS);
+        test("-84.0false", "" + sf_dM + f_bl);
+        test("-84.0null", "" + sf_dM + sf_iAN);
+        test("-84.0-2000000", "" + sf_dM + sf_iM);
+        test("-84.0-820130816", "" + sf_dM + f_lM);
+        test("-84.0null", "" + sf_dM + sf_oAN);
+        test("-84.025000000", "" + sf_dM + s_I);
+        test("58000000-96.0", "" + f_i + s_dM);
+        test("58000000null", "" + f_i + s_oNtS);
+        test("58000000\u045176", "" + f_i + f_strU);
+        test("5800000092", "" + f_i + sf_strU2);
+        test("5800000051", "" + f_i + sf_strU1);
+        test("58000000null", "" + f_i + s_iAN);
+        test("58000000-54", "" + f_i + f_bM);
+        test("58000000-87.0", "" + f_i + f_fM);
+        test("58000000null", "" + f_i + s_oAN);
+        test("5800000019", "" + f_i + f_str);
+        test("58000000-41", "" + f_i + sf_bM);
+        test("58000000null", "" + f_i + sf_IN);
+        test("58000000T", "" + f_i + s_c);
+        test("58000000-42.0", "" + f_i + sf_fM);
+        test("5800000025", "" + f_i + s_b);
+        test("58000000null", "" + f_i + f_oN);
+        test("58000000-1410065408", "" + f_i + s_lM);
+        test("580000008.0", "" + f_i + s_d);
+        test("5800000055.0", "" + f_i + s_f);
+        test("5800000097000000", "" + f_i + s_i);
+        test("58000000-9900", "" + f_i + f_sM);
+        test("58000000935228928", "" + f_i + s_l);
+        test("58000000-8400", "" + f_i + sf_sM);
+        test("58000000C(82)", "" + f_i + s_o);
+        test("58000000null", "" + f_i + sf_oNtS);
+        test("58000000true", "" + f_i + s_bl);
+        test("580000003900", "" + f_i + s_s);
+        test("58000000null", "" + f_i + sf_oN);
+        test("5800000094000000", "" + f_i + f_I);
+        test("58000000null", "" + f_i + f_IN);
+        test("58000000true", "" + f_i + sf_bl);
+        test("580000005500", "" + f_i + sf_s);
+        test("58000000-2900", "" + f_i + s_sM);
+        test("58000000-194313216", "" + f_i + sf_l);
+        test("5800000012", "" + f_i + s_strU1);
+        test("58000000C(87)", "" + f_i + sf_o);
+        test("5800000091", "" + f_i + s_strU2);
+        test("5800000021", "" + f_i + f_strU1);
+        test("5800000018", "" + f_i + f_strU2);
+        test("58000000null", "" + f_i + f_iAN);
+        test("58000000null", "" + f_i + s_oN);
+        test("58000000\u045180", "" + f_i + s_strU);
+        test("58000000C", "" + f_i + sf_c);
+        test("5800000075", "" + f_i + sf_str);
+        test("58000000-43", "" + f_i + s_bM);
+        test("5800000080", "" + f_i + sf_b);
+        test("58000000null", "" + f_i + s_IN);
+        test("58000000-52.0", "" + f_i + s_fM);
+        test("5800000075000000", "" + f_i + sf_i);
+        test("5800000044", "" + f_i + f_b);
+        test("58000000-1705032704", "" + f_i + sf_lM);
+        test("58000000null", "" + f_i + f_oAN);
+        test("5800000083.0", "" + f_i + f_d);
+        test("58000000I", "" + f_i + f_c);
+        test("5800000094.0", "" + f_i + f_f);
+        test("5800000012.0", "" + f_i + sf_d);
+        test("58000000-99.0", "" + f_i + f_dM);
+        test("5800000017.0", "" + f_i + sf_f);
+        test("58000000-84.0", "" + f_i + sf_dM);
+        test("5800000058000000", "" + f_i + f_i);
+        test("58000000-55000000", "" + f_i + f_iM);
+        test("580000001460392448", "" + f_i + f_l);
+        test("58000000C(70)", "" + f_i + f_o);
+        test("58000000\u04511", "" + f_i + sf_strU);
+        test("580000008000", "" + f_i + f_s);
+        test("5800000018", "" + f_i + s_str);
+        test("58000000-1000000", "" + f_i + s_iM);
+        test("580000001000000", "" + f_i + sf_I);
+        test("58000000null", "" + f_i + f_oNtS);
+        test("58000000false", "" + f_i + f_bl);
+        test("58000000null", "" + f_i + sf_iAN);
+        test("58000000-2000000", "" + f_i + sf_iM);
+        test("58000000-820130816", "" + f_i + f_lM);
+        test("58000000null", "" + f_i + sf_oAN);
+        test("5800000025000000", "" + f_i + s_I);
+        test("-55000000-96.0", "" + f_iM + s_dM);
+        test("-55000000null", "" + f_iM + s_oNtS);
+        test("-55000000\u045176", "" + f_iM + f_strU);
+        test("-5500000092", "" + f_iM + sf_strU2);
+        test("-5500000051", "" + f_iM + sf_strU1);
+        test("-55000000null", "" + f_iM + s_iAN);
+        test("-55000000-54", "" + f_iM + f_bM);
+        test("-55000000-87.0", "" + f_iM + f_fM);
+        test("-55000000null", "" + f_iM + s_oAN);
+        test("-5500000019", "" + f_iM + f_str);
+        test("-55000000-41", "" + f_iM + sf_bM);
+        test("-55000000null", "" + f_iM + sf_IN);
+        test("-55000000T", "" + f_iM + s_c);
+        test("-55000000-42.0", "" + f_iM + sf_fM);
+        test("-5500000025", "" + f_iM + s_b);
+        test("-55000000null", "" + f_iM + f_oN);
+        test("-55000000-1410065408", "" + f_iM + s_lM);
+        test("-550000008.0", "" + f_iM + s_d);
+        test("-5500000055.0", "" + f_iM + s_f);
+        test("-5500000097000000", "" + f_iM + s_i);
+        test("-55000000-9900", "" + f_iM + f_sM);
+        test("-55000000935228928", "" + f_iM + s_l);
+        test("-55000000-8400", "" + f_iM + sf_sM);
+        test("-55000000C(82)", "" + f_iM + s_o);
+        test("-55000000null", "" + f_iM + sf_oNtS);
+        test("-55000000true", "" + f_iM + s_bl);
+        test("-550000003900", "" + f_iM + s_s);
+        test("-55000000null", "" + f_iM + sf_oN);
+        test("-5500000094000000", "" + f_iM + f_I);
+        test("-55000000null", "" + f_iM + f_IN);
+        test("-55000000true", "" + f_iM + sf_bl);
+        test("-550000005500", "" + f_iM + sf_s);
+        test("-55000000-2900", "" + f_iM + s_sM);
+        test("-55000000-194313216", "" + f_iM + sf_l);
+        test("-5500000012", "" + f_iM + s_strU1);
+        test("-55000000C(87)", "" + f_iM + sf_o);
+        test("-5500000091", "" + f_iM + s_strU2);
+        test("-5500000021", "" + f_iM + f_strU1);
+        test("-5500000018", "" + f_iM + f_strU2);
+        test("-55000000null", "" + f_iM + f_iAN);
+        test("-55000000null", "" + f_iM + s_oN);
+        test("-55000000\u045180", "" + f_iM + s_strU);
+        test("-55000000C", "" + f_iM + sf_c);
+        test("-5500000075", "" + f_iM + sf_str);
+        test("-55000000-43", "" + f_iM + s_bM);
+        test("-5500000080", "" + f_iM + sf_b);
+        test("-55000000null", "" + f_iM + s_IN);
+        test("-55000000-52.0", "" + f_iM + s_fM);
+        test("-5500000075000000", "" + f_iM + sf_i);
+        test("-5500000044", "" + f_iM + f_b);
+        test("-55000000-1705032704", "" + f_iM + sf_lM);
+        test("-55000000null", "" + f_iM + f_oAN);
+        test("-5500000083.0", "" + f_iM + f_d);
+        test("-55000000I", "" + f_iM + f_c);
+        test("-5500000094.0", "" + f_iM + f_f);
+        test("-5500000012.0", "" + f_iM + sf_d);
+        test("-55000000-99.0", "" + f_iM + f_dM);
+        test("-5500000017.0", "" + f_iM + sf_f);
+        test("-55000000-84.0", "" + f_iM + sf_dM);
+        test("-5500000058000000", "" + f_iM + f_i);
+        test("-55000000-55000000", "" + f_iM + f_iM);
+        test("-550000001460392448", "" + f_iM + f_l);
+        test("-55000000C(70)", "" + f_iM + f_o);
+        test("-55000000\u04511", "" + f_iM + sf_strU);
+        test("-550000008000", "" + f_iM + f_s);
+        test("-5500000018", "" + f_iM + s_str);
+        test("-55000000-1000000", "" + f_iM + s_iM);
+        test("-550000001000000", "" + f_iM + sf_I);
+        test("-55000000null", "" + f_iM + f_oNtS);
+        test("-55000000false", "" + f_iM + f_bl);
+        test("-55000000null", "" + f_iM + sf_iAN);
+        test("-55000000-2000000", "" + f_iM + sf_iM);
+        test("-55000000-820130816", "" + f_iM + f_lM);
+        test("-55000000null", "" + f_iM + sf_oAN);
+        test("-5500000025000000", "" + f_iM + s_I);
+        test("1460392448-96.0", "" + f_l + s_dM);
+        test("1460392448null", "" + f_l + s_oNtS);
+        test("1460392448\u045176", "" + f_l + f_strU);
+        test("146039244892", "" + f_l + sf_strU2);
+        test("146039244851", "" + f_l + sf_strU1);
+        test("1460392448null", "" + f_l + s_iAN);
+        test("1460392448-54", "" + f_l + f_bM);
+        test("1460392448-87.0", "" + f_l + f_fM);
+        test("1460392448null", "" + f_l + s_oAN);
+        test("146039244819", "" + f_l + f_str);
+        test("1460392448-41", "" + f_l + sf_bM);
+        test("1460392448null", "" + f_l + sf_IN);
+        test("1460392448T", "" + f_l + s_c);
+        test("1460392448-42.0", "" + f_l + sf_fM);
+        test("146039244825", "" + f_l + s_b);
+        test("1460392448null", "" + f_l + f_oN);
+        test("1460392448-1410065408", "" + f_l + s_lM);
+        test("14603924488.0", "" + f_l + s_d);
+        test("146039244855.0", "" + f_l + s_f);
+        test("146039244897000000", "" + f_l + s_i);
+        test("1460392448-9900", "" + f_l + f_sM);
+        test("1460392448935228928", "" + f_l + s_l);
+        test("1460392448-8400", "" + f_l + sf_sM);
+        test("1460392448C(82)", "" + f_l + s_o);
+        test("1460392448null", "" + f_l + sf_oNtS);
+        test("1460392448true", "" + f_l + s_bl);
+        test("14603924483900", "" + f_l + s_s);
+        test("1460392448null", "" + f_l + sf_oN);
+        test("146039244894000000", "" + f_l + f_I);
+        test("1460392448null", "" + f_l + f_IN);
+        test("1460392448true", "" + f_l + sf_bl);
+        test("14603924485500", "" + f_l + sf_s);
+        test("1460392448-2900", "" + f_l + s_sM);
+        test("1460392448-194313216", "" + f_l + sf_l);
+        test("146039244812", "" + f_l + s_strU1);
+        test("1460392448C(87)", "" + f_l + sf_o);
+        test("146039244891", "" + f_l + s_strU2);
+        test("146039244821", "" + f_l + f_strU1);
+        test("146039244818", "" + f_l + f_strU2);
+        test("1460392448null", "" + f_l + f_iAN);
+        test("1460392448null", "" + f_l + s_oN);
+        test("1460392448\u045180", "" + f_l + s_strU);
+        test("1460392448C", "" + f_l + sf_c);
+        test("146039244875", "" + f_l + sf_str);
+        test("1460392448-43", "" + f_l + s_bM);
+        test("146039244880", "" + f_l + sf_b);
+        test("1460392448null", "" + f_l + s_IN);
+        test("1460392448-52.0", "" + f_l + s_fM);
+        test("146039244875000000", "" + f_l + sf_i);
+        test("146039244844", "" + f_l + f_b);
+        test("1460392448-1705032704", "" + f_l + sf_lM);
+        test("1460392448null", "" + f_l + f_oAN);
+        test("146039244883.0", "" + f_l + f_d);
+        test("1460392448I", "" + f_l + f_c);
+        test("146039244894.0", "" + f_l + f_f);
+        test("146039244812.0", "" + f_l + sf_d);
+        test("1460392448-99.0", "" + f_l + f_dM);
+        test("146039244817.0", "" + f_l + sf_f);
+        test("1460392448-84.0", "" + f_l + sf_dM);
+        test("146039244858000000", "" + f_l + f_i);
+        test("1460392448-55000000", "" + f_l + f_iM);
+        test("14603924481460392448", "" + f_l + f_l);
+        test("1460392448C(70)", "" + f_l + f_o);
+        test("1460392448\u04511", "" + f_l + sf_strU);
+        test("14603924488000", "" + f_l + f_s);
+        test("146039244818", "" + f_l + s_str);
+        test("1460392448-1000000", "" + f_l + s_iM);
+        test("14603924481000000", "" + f_l + sf_I);
+        test("1460392448null", "" + f_l + f_oNtS);
+        test("1460392448false", "" + f_l + f_bl);
+        test("1460392448null", "" + f_l + sf_iAN);
+        test("1460392448-2000000", "" + f_l + sf_iM);
+        test("1460392448-820130816", "" + f_l + f_lM);
+        test("1460392448null", "" + f_l + sf_oAN);
+        test("146039244825000000", "" + f_l + s_I);
+        test("C(70)-96.0", "" + f_o + s_dM);
+        test("C(70)null", "" + f_o + s_oNtS);
+        test("C(70)\u045176", "" + f_o + f_strU);
+        test("C(70)92", "" + f_o + sf_strU2);
+        test("C(70)51", "" + f_o + sf_strU1);
+        test("C(70)null", "" + f_o + s_iAN);
+        test("C(70)-54", "" + f_o + f_bM);
+        test("C(70)-87.0", "" + f_o + f_fM);
+        test("C(70)null", "" + f_o + s_oAN);
+        test("C(70)19", "" + f_o + f_str);
+        test("C(70)-41", "" + f_o + sf_bM);
+        test("C(70)null", "" + f_o + sf_IN);
+        test("C(70)T", "" + f_o + s_c);
+        test("C(70)-42.0", "" + f_o + sf_fM);
+        test("C(70)25", "" + f_o + s_b);
+        test("C(70)null", "" + f_o + f_oN);
+        test("C(70)-1410065408", "" + f_o + s_lM);
+        test("C(70)8.0", "" + f_o + s_d);
+        test("C(70)55.0", "" + f_o + s_f);
+        test("C(70)97000000", "" + f_o + s_i);
+        test("C(70)-9900", "" + f_o + f_sM);
+        test("C(70)935228928", "" + f_o + s_l);
+        test("C(70)-8400", "" + f_o + sf_sM);
+        test("C(70)C(82)", "" + f_o + s_o);
+        test("C(70)null", "" + f_o + sf_oNtS);
+        test("C(70)true", "" + f_o + s_bl);
+        test("C(70)3900", "" + f_o + s_s);
+        test("C(70)null", "" + f_o + sf_oN);
+        test("C(70)94000000", "" + f_o + f_I);
+        test("C(70)null", "" + f_o + f_IN);
+        test("C(70)true", "" + f_o + sf_bl);
+        test("C(70)5500", "" + f_o + sf_s);
+        test("C(70)-2900", "" + f_o + s_sM);
+        test("C(70)-194313216", "" + f_o + sf_l);
+        test("C(70)12", "" + f_o + s_strU1);
+        test("C(70)C(87)", "" + f_o + sf_o);
+        test("C(70)91", "" + f_o + s_strU2);
+        test("C(70)21", "" + f_o + f_strU1);
+        test("C(70)18", "" + f_o + f_strU2);
+        test("C(70)null", "" + f_o + f_iAN);
+        test("C(70)null", "" + f_o + s_oN);
+        test("C(70)\u045180", "" + f_o + s_strU);
+        test("C(70)C", "" + f_o + sf_c);
+        test("C(70)75", "" + f_o + sf_str);
+        test("C(70)-43", "" + f_o + s_bM);
+        test("C(70)80", "" + f_o + sf_b);
+        test("C(70)null", "" + f_o + s_IN);
+        test("C(70)-52.0", "" + f_o + s_fM);
+        test("C(70)75000000", "" + f_o + sf_i);
+        test("C(70)44", "" + f_o + f_b);
+        test("C(70)-1705032704", "" + f_o + sf_lM);
+        test("C(70)null", "" + f_o + f_oAN);
+        test("C(70)83.0", "" + f_o + f_d);
+        test("C(70)I", "" + f_o + f_c);
+        test("C(70)94.0", "" + f_o + f_f);
+        test("C(70)12.0", "" + f_o + sf_d);
+        test("C(70)-99.0", "" + f_o + f_dM);
+        test("C(70)17.0", "" + f_o + sf_f);
+        test("C(70)-84.0", "" + f_o + sf_dM);
+        test("C(70)58000000", "" + f_o + f_i);
+        test("C(70)-55000000", "" + f_o + f_iM);
+        test("C(70)1460392448", "" + f_o + f_l);
+        test("C(70)C(70)", "" + f_o + f_o);
+        test("C(70)\u04511", "" + f_o + sf_strU);
+        test("C(70)8000", "" + f_o + f_s);
+        test("C(70)18", "" + f_o + s_str);
+        test("C(70)-1000000", "" + f_o + s_iM);
+        test("C(70)1000000", "" + f_o + sf_I);
+        test("C(70)null", "" + f_o + f_oNtS);
+        test("C(70)false", "" + f_o + f_bl);
+        test("C(70)null", "" + f_o + sf_iAN);
+        test("C(70)-2000000", "" + f_o + sf_iM);
+        test("C(70)-820130816", "" + f_o + f_lM);
+        test("C(70)null", "" + f_o + sf_oAN);
+        test("C(70)25000000", "" + f_o + s_I);
+        test("\u04511-96.0", "" + sf_strU + s_dM);
+        test("\u04511null", "" + sf_strU + s_oNtS);
+        test("\u04511\u045176", "" + sf_strU + f_strU);
+        test("\u0451192", "" + sf_strU + sf_strU2);
+        test("\u0451151", "" + sf_strU + sf_strU1);
+        test("\u04511null", "" + sf_strU + s_iAN);
+        test("\u04511-54", "" + sf_strU + f_bM);
+        test("\u04511-87.0", "" + sf_strU + f_fM);
+        test("\u04511null", "" + sf_strU + s_oAN);
+        test("\u0451119", "" + sf_strU + f_str);
+        test("\u04511-41", "" + sf_strU + sf_bM);
+        test("\u04511null", "" + sf_strU + sf_IN);
+        test("\u04511T", "" + sf_strU + s_c);
+        test("\u04511-42.0", "" + sf_strU + sf_fM);
+        test("\u0451125", "" + sf_strU + s_b);
+        test("\u04511null", "" + sf_strU + f_oN);
+        test("\u04511-1410065408", "" + sf_strU + s_lM);
+        test("\u045118.0", "" + sf_strU + s_d);
+        test("\u0451155.0", "" + sf_strU + s_f);
+        test("\u0451197000000", "" + sf_strU + s_i);
+        test("\u04511-9900", "" + sf_strU + f_sM);
+        test("\u04511935228928", "" + sf_strU + s_l);
+        test("\u04511-8400", "" + sf_strU + sf_sM);
+        test("\u04511C(82)", "" + sf_strU + s_o);
+        test("\u04511null", "" + sf_strU + sf_oNtS);
+        test("\u04511true", "" + sf_strU + s_bl);
+        test("\u045113900", "" + sf_strU + s_s);
+        test("\u04511null", "" + sf_strU + sf_oN);
+        test("\u0451194000000", "" + sf_strU + f_I);
+        test("\u04511null", "" + sf_strU + f_IN);
+        test("\u04511true", "" + sf_strU + sf_bl);
+        test("\u045115500", "" + sf_strU + sf_s);
+        test("\u04511-2900", "" + sf_strU + s_sM);
+        test("\u04511-194313216", "" + sf_strU + sf_l);
+        test("\u0451112", "" + sf_strU + s_strU1);
+        test("\u04511C(87)", "" + sf_strU + sf_o);
+        test("\u0451191", "" + sf_strU + s_strU2);
+        test("\u0451121", "" + sf_strU + f_strU1);
+        test("\u0451118", "" + sf_strU + f_strU2);
+        test("\u04511null", "" + sf_strU + f_iAN);
+        test("\u04511null", "" + sf_strU + s_oN);
+        test("\u04511\u045180", "" + sf_strU + s_strU);
+        test("\u04511C", "" + sf_strU + sf_c);
+        test("\u0451175", "" + sf_strU + sf_str);
+        test("\u04511-43", "" + sf_strU + s_bM);
+        test("\u0451180", "" + sf_strU + sf_b);
+        test("\u04511null", "" + sf_strU + s_IN);
+        test("\u04511-52.0", "" + sf_strU + s_fM);
+        test("\u0451175000000", "" + sf_strU + sf_i);
+        test("\u0451144", "" + sf_strU + f_b);
+        test("\u04511-1705032704", "" + sf_strU + sf_lM);
+        test("\u04511null", "" + sf_strU + f_oAN);
+        test("\u0451183.0", "" + sf_strU + f_d);
+        test("\u04511I", "" + sf_strU + f_c);
+        test("\u0451194.0", "" + sf_strU + f_f);
+        test("\u0451112.0", "" + sf_strU + sf_d);
+        test("\u04511-99.0", "" + sf_strU + f_dM);
+        test("\u0451117.0", "" + sf_strU + sf_f);
+        test("\u04511-84.0", "" + sf_strU + sf_dM);
+        test("\u0451158000000", "" + sf_strU + f_i);
+        test("\u04511-55000000", "" + sf_strU + f_iM);
+        test("\u045111460392448", "" + sf_strU + f_l);
+        test("\u04511C(70)", "" + sf_strU + f_o);
+        test("\u04511\u04511", "" + sf_strU + sf_strU);
+        test("\u045118000", "" + sf_strU + f_s);
+        test("\u0451118", "" + sf_strU + s_str);
+        test("\u04511-1000000", "" + sf_strU + s_iM);
+        test("\u045111000000", "" + sf_strU + sf_I);
+        test("\u04511null", "" + sf_strU + f_oNtS);
+        test("\u04511false", "" + sf_strU + f_bl);
+        test("\u04511null", "" + sf_strU + sf_iAN);
+        test("\u04511-2000000", "" + sf_strU + sf_iM);
+        test("\u04511-820130816", "" + sf_strU + f_lM);
+        test("\u04511null", "" + sf_strU + sf_oAN);
+        test("\u0451125000000", "" + sf_strU + s_I);
+        test("8000-96.0", "" + f_s + s_dM);
+        test("8000null", "" + f_s + s_oNtS);
+        test("8000\u045176", "" + f_s + f_strU);
+        test("800092", "" + f_s + sf_strU2);
+        test("800051", "" + f_s + sf_strU1);
+        test("8000null", "" + f_s + s_iAN);
+        test("8000-54", "" + f_s + f_bM);
+        test("8000-87.0", "" + f_s + f_fM);
+        test("8000null", "" + f_s + s_oAN);
+        test("800019", "" + f_s + f_str);
+        test("8000-41", "" + f_s + sf_bM);
+        test("8000null", "" + f_s + sf_IN);
+        test("8000T", "" + f_s + s_c);
+        test("8000-42.0", "" + f_s + sf_fM);
+        test("800025", "" + f_s + s_b);
+        test("8000null", "" + f_s + f_oN);
+        test("8000-1410065408", "" + f_s + s_lM);
+        test("80008.0", "" + f_s + s_d);
+        test("800055.0", "" + f_s + s_f);
+        test("800097000000", "" + f_s + s_i);
+        test("8000-9900", "" + f_s + f_sM);
+        test("8000935228928", "" + f_s + s_l);
+        test("8000-8400", "" + f_s + sf_sM);
+        test("8000C(82)", "" + f_s + s_o);
+        test("8000null", "" + f_s + sf_oNtS);
+        test("8000true", "" + f_s + s_bl);
+        test("80003900", "" + f_s + s_s);
+        test("8000null", "" + f_s + sf_oN);
+        test("800094000000", "" + f_s + f_I);
+        test("8000null", "" + f_s + f_IN);
+        test("8000true", "" + f_s + sf_bl);
+        test("80005500", "" + f_s + sf_s);
+        test("8000-2900", "" + f_s + s_sM);
+        test("8000-194313216", "" + f_s + sf_l);
+        test("800012", "" + f_s + s_strU1);
+        test("8000C(87)", "" + f_s + sf_o);
+        test("800091", "" + f_s + s_strU2);
+        test("800021", "" + f_s + f_strU1);
+        test("800018", "" + f_s + f_strU2);
+        test("8000null", "" + f_s + f_iAN);
+        test("8000null", "" + f_s + s_oN);
+        test("8000\u045180", "" + f_s + s_strU);
+        test("8000C", "" + f_s + sf_c);
+        test("800075", "" + f_s + sf_str);
+        test("8000-43", "" + f_s + s_bM);
+        test("800080", "" + f_s + sf_b);
+        test("8000null", "" + f_s + s_IN);
+        test("8000-52.0", "" + f_s + s_fM);
+        test("800075000000", "" + f_s + sf_i);
+        test("800044", "" + f_s + f_b);
+        test("8000-1705032704", "" + f_s + sf_lM);
+        test("8000null", "" + f_s + f_oAN);
+        test("800083.0", "" + f_s + f_d);
+        test("8000I", "" + f_s + f_c);
+        test("800094.0", "" + f_s + f_f);
+        test("800012.0", "" + f_s + sf_d);
+        test("8000-99.0", "" + f_s + f_dM);
+        test("800017.0", "" + f_s + sf_f);
+        test("8000-84.0", "" + f_s + sf_dM);
+        test("800058000000", "" + f_s + f_i);
+        test("8000-55000000", "" + f_s + f_iM);
+        test("80001460392448", "" + f_s + f_l);
+        test("8000C(70)", "" + f_s + f_o);
+        test("8000\u04511", "" + f_s + sf_strU);
+        test("80008000", "" + f_s + f_s);
+        test("800018", "" + f_s + s_str);
+        test("8000-1000000", "" + f_s + s_iM);
+        test("80001000000", "" + f_s + sf_I);
+        test("8000null", "" + f_s + f_oNtS);
+        test("8000false", "" + f_s + f_bl);
+        test("8000null", "" + f_s + sf_iAN);
+        test("8000-2000000", "" + f_s + sf_iM);
+        test("8000-820130816", "" + f_s + f_lM);
+        test("8000null", "" + f_s + sf_oAN);
+        test("800025000000", "" + f_s + s_I);
+        test("18-96.0", "" + s_str + s_dM);
+        test("18null", "" + s_str + s_oNtS);
+        test("18\u045176", "" + s_str + f_strU);
+        test("1892", "" + s_str + sf_strU2);
+        test("1851", "" + s_str + sf_strU1);
+        test("18null", "" + s_str + s_iAN);
+        test("18-54", "" + s_str + f_bM);
+        test("18-87.0", "" + s_str + f_fM);
+        test("18null", "" + s_str + s_oAN);
+        test("1819", "" + s_str + f_str);
+        test("18-41", "" + s_str + sf_bM);
+        test("18null", "" + s_str + sf_IN);
+        test("18T", "" + s_str + s_c);
+        test("18-42.0", "" + s_str + sf_fM);
+        test("1825", "" + s_str + s_b);
+        test("18null", "" + s_str + f_oN);
+        test("18-1410065408", "" + s_str + s_lM);
+        test("188.0", "" + s_str + s_d);
+        test("1855.0", "" + s_str + s_f);
+        test("1897000000", "" + s_str + s_i);
+        test("18-9900", "" + s_str + f_sM);
+        test("18935228928", "" + s_str + s_l);
+        test("18-8400", "" + s_str + sf_sM);
+        test("18C(82)", "" + s_str + s_o);
+        test("18null", "" + s_str + sf_oNtS);
+        test("18true", "" + s_str + s_bl);
+        test("183900", "" + s_str + s_s);
+        test("18null", "" + s_str + sf_oN);
+        test("1894000000", "" + s_str + f_I);
+        test("18null", "" + s_str + f_IN);
+        test("18true", "" + s_str + sf_bl);
+        test("185500", "" + s_str + sf_s);
+        test("18-2900", "" + s_str + s_sM);
+        test("18-194313216", "" + s_str + sf_l);
+        test("1812", "" + s_str + s_strU1);
+        test("18C(87)", "" + s_str + sf_o);
+        test("1891", "" + s_str + s_strU2);
+        test("1821", "" + s_str + f_strU1);
+        test("1818", "" + s_str + f_strU2);
+        test("18null", "" + s_str + f_iAN);
+        test("18null", "" + s_str + s_oN);
+        test("18\u045180", "" + s_str + s_strU);
+        test("18C", "" + s_str + sf_c);
+        test("1875", "" + s_str + sf_str);
+        test("18-43", "" + s_str + s_bM);
+        test("1880", "" + s_str + sf_b);
+        test("18null", "" + s_str + s_IN);
+        test("18-52.0", "" + s_str + s_fM);
+        test("1875000000", "" + s_str + sf_i);
+        test("1844", "" + s_str + f_b);
+    }
+
+    public void run5() {
+        test("18-1705032704", "" + s_str + sf_lM);
+        test("18null", "" + s_str + f_oAN);
+        test("1883.0", "" + s_str + f_d);
+        test("18I", "" + s_str + f_c);
+        test("1894.0", "" + s_str + f_f);
+        test("1812.0", "" + s_str + sf_d);
+        test("18-99.0", "" + s_str + f_dM);
+        test("1817.0", "" + s_str + sf_f);
+        test("18-84.0", "" + s_str + sf_dM);
+        test("1858000000", "" + s_str + f_i);
+        test("18-55000000", "" + s_str + f_iM);
+        test("181460392448", "" + s_str + f_l);
+        test("18C(70)", "" + s_str + f_o);
+        test("18\u04511", "" + s_str + sf_strU);
+        test("188000", "" + s_str + f_s);
+        test("1818", "" + s_str + s_str);
+        test("18-1000000", "" + s_str + s_iM);
+        test("181000000", "" + s_str + sf_I);
+        test("18null", "" + s_str + f_oNtS);
+        test("18false", "" + s_str + f_bl);
+        test("18null", "" + s_str + sf_iAN);
+        test("18-2000000", "" + s_str + sf_iM);
+        test("18-820130816", "" + s_str + f_lM);
+        test("18null", "" + s_str + sf_oAN);
+        test("1825000000", "" + s_str + s_I);
+        test("-1000000-96.0", "" + s_iM + s_dM);
+        test("-1000000null", "" + s_iM + s_oNtS);
+        test("-1000000\u045176", "" + s_iM + f_strU);
+        test("-100000092", "" + s_iM + sf_strU2);
+        test("-100000051", "" + s_iM + sf_strU1);
+        test("-1000000null", "" + s_iM + s_iAN);
+        test("-1000000-54", "" + s_iM + f_bM);
+        test("-1000000-87.0", "" + s_iM + f_fM);
+        test("-1000000null", "" + s_iM + s_oAN);
+        test("-100000019", "" + s_iM + f_str);
+        test("-1000000-41", "" + s_iM + sf_bM);
+        test("-1000000null", "" + s_iM + sf_IN);
+        test("-1000000T", "" + s_iM + s_c);
+        test("-1000000-42.0", "" + s_iM + sf_fM);
+        test("-100000025", "" + s_iM + s_b);
+        test("-1000000null", "" + s_iM + f_oN);
+        test("-1000000-1410065408", "" + s_iM + s_lM);
+        test("-10000008.0", "" + s_iM + s_d);
+        test("-100000055.0", "" + s_iM + s_f);
+        test("-100000097000000", "" + s_iM + s_i);
+        test("-1000000-9900", "" + s_iM + f_sM);
+        test("-1000000935228928", "" + s_iM + s_l);
+        test("-1000000-8400", "" + s_iM + sf_sM);
+        test("-1000000C(82)", "" + s_iM + s_o);
+        test("-1000000null", "" + s_iM + sf_oNtS);
+        test("-1000000true", "" + s_iM + s_bl);
+        test("-10000003900", "" + s_iM + s_s);
+        test("-1000000null", "" + s_iM + sf_oN);
+        test("-100000094000000", "" + s_iM + f_I);
+        test("-1000000null", "" + s_iM + f_IN);
+        test("-1000000true", "" + s_iM + sf_bl);
+        test("-10000005500", "" + s_iM + sf_s);
+        test("-1000000-2900", "" + s_iM + s_sM);
+        test("-1000000-194313216", "" + s_iM + sf_l);
+        test("-100000012", "" + s_iM + s_strU1);
+        test("-1000000C(87)", "" + s_iM + sf_o);
+        test("-100000091", "" + s_iM + s_strU2);
+        test("-100000021", "" + s_iM + f_strU1);
+        test("-100000018", "" + s_iM + f_strU2);
+        test("-1000000null", "" + s_iM + f_iAN);
+        test("-1000000null", "" + s_iM + s_oN);
+        test("-1000000\u045180", "" + s_iM + s_strU);
+        test("-1000000C", "" + s_iM + sf_c);
+        test("-100000075", "" + s_iM + sf_str);
+        test("-1000000-43", "" + s_iM + s_bM);
+        test("-100000080", "" + s_iM + sf_b);
+        test("-1000000null", "" + s_iM + s_IN);
+        test("-1000000-52.0", "" + s_iM + s_fM);
+        test("-100000075000000", "" + s_iM + sf_i);
+        test("-100000044", "" + s_iM + f_b);
+        test("-1000000-1705032704", "" + s_iM + sf_lM);
+        test("-1000000null", "" + s_iM + f_oAN);
+        test("-100000083.0", "" + s_iM + f_d);
+        test("-1000000I", "" + s_iM + f_c);
+        test("-100000094.0", "" + s_iM + f_f);
+        test("-100000012.0", "" + s_iM + sf_d);
+        test("-1000000-99.0", "" + s_iM + f_dM);
+        test("-100000017.0", "" + s_iM + sf_f);
+        test("-1000000-84.0", "" + s_iM + sf_dM);
+        test("-100000058000000", "" + s_iM + f_i);
+        test("-1000000-55000000", "" + s_iM + f_iM);
+        test("-10000001460392448", "" + s_iM + f_l);
+        test("-1000000C(70)", "" + s_iM + f_o);
+        test("-1000000\u04511", "" + s_iM + sf_strU);
+        test("-10000008000", "" + s_iM + f_s);
+        test("-100000018", "" + s_iM + s_str);
+        test("-1000000-1000000", "" + s_iM + s_iM);
+        test("-10000001000000", "" + s_iM + sf_I);
+        test("-1000000null", "" + s_iM + f_oNtS);
+        test("-1000000false", "" + s_iM + f_bl);
+        test("-1000000null", "" + s_iM + sf_iAN);
+        test("-1000000-2000000", "" + s_iM + sf_iM);
+        test("-1000000-820130816", "" + s_iM + f_lM);
+        test("-1000000null", "" + s_iM + sf_oAN);
+        test("-100000025000000", "" + s_iM + s_I);
+        test("1000000-96.0", "" + sf_I + s_dM);
+        test("1000000null", "" + sf_I + s_oNtS);
+        test("1000000\u045176", "" + sf_I + f_strU);
+        test("100000092", "" + sf_I + sf_strU2);
+        test("100000051", "" + sf_I + sf_strU1);
+        test("1000000null", "" + sf_I + s_iAN);
+        test("1000000-54", "" + sf_I + f_bM);
+        test("1000000-87.0", "" + sf_I + f_fM);
+        test("1000000null", "" + sf_I + s_oAN);
+        test("100000019", "" + sf_I + f_str);
+        test("1000000-41", "" + sf_I + sf_bM);
+        test("1000000null", "" + sf_I + sf_IN);
+        test("1000000T", "" + sf_I + s_c);
+        test("1000000-42.0", "" + sf_I + sf_fM);
+        test("100000025", "" + sf_I + s_b);
+        test("1000000null", "" + sf_I + f_oN);
+        test("1000000-1410065408", "" + sf_I + s_lM);
+        test("10000008.0", "" + sf_I + s_d);
+        test("100000055.0", "" + sf_I + s_f);
+        test("100000097000000", "" + sf_I + s_i);
+        test("1000000-9900", "" + sf_I + f_sM);
+        test("1000000935228928", "" + sf_I + s_l);
+        test("1000000-8400", "" + sf_I + sf_sM);
+        test("1000000C(82)", "" + sf_I + s_o);
+        test("1000000null", "" + sf_I + sf_oNtS);
+        test("1000000true", "" + sf_I + s_bl);
+        test("10000003900", "" + sf_I + s_s);
+        test("1000000null", "" + sf_I + sf_oN);
+        test("100000094000000", "" + sf_I + f_I);
+        test("1000000null", "" + sf_I + f_IN);
+        test("1000000true", "" + sf_I + sf_bl);
+        test("10000005500", "" + sf_I + sf_s);
+        test("1000000-2900", "" + sf_I + s_sM);
+        test("1000000-194313216", "" + sf_I + sf_l);
+        test("100000012", "" + sf_I + s_strU1);
+        test("1000000C(87)", "" + sf_I + sf_o);
+        test("100000091", "" + sf_I + s_strU2);
+        test("100000021", "" + sf_I + f_strU1);
+        test("100000018", "" + sf_I + f_strU2);
+        test("1000000null", "" + sf_I + f_iAN);
+        test("1000000null", "" + sf_I + s_oN);
+        test("1000000\u045180", "" + sf_I + s_strU);
+        test("1000000C", "" + sf_I + sf_c);
+        test("100000075", "" + sf_I + sf_str);
+        test("1000000-43", "" + sf_I + s_bM);
+        test("100000080", "" + sf_I + sf_b);
+        test("1000000null", "" + sf_I + s_IN);
+        test("1000000-52.0", "" + sf_I + s_fM);
+        test("100000075000000", "" + sf_I + sf_i);
+        test("100000044", "" + sf_I + f_b);
+        test("1000000-1705032704", "" + sf_I + sf_lM);
+        test("1000000null", "" + sf_I + f_oAN);
+        test("100000083.0", "" + sf_I + f_d);
+        test("1000000I", "" + sf_I + f_c);
+        test("100000094.0", "" + sf_I + f_f);
+        test("100000012.0", "" + sf_I + sf_d);
+        test("1000000-99.0", "" + sf_I + f_dM);
+        test("100000017.0", "" + sf_I + sf_f);
+        test("1000000-84.0", "" + sf_I + sf_dM);
+        test("100000058000000", "" + sf_I + f_i);
+        test("1000000-55000000", "" + sf_I + f_iM);
+        test("10000001460392448", "" + sf_I + f_l);
+        test("1000000C(70)", "" + sf_I + f_o);
+        test("1000000\u04511", "" + sf_I + sf_strU);
+        test("10000008000", "" + sf_I + f_s);
+        test("100000018", "" + sf_I + s_str);
+        test("1000000-1000000", "" + sf_I + s_iM);
+        test("10000001000000", "" + sf_I + sf_I);
+        test("1000000null", "" + sf_I + f_oNtS);
+        test("1000000false", "" + sf_I + f_bl);
+        test("1000000null", "" + sf_I + sf_iAN);
+        test("1000000-2000000", "" + sf_I + sf_iM);
+        test("1000000-820130816", "" + sf_I + f_lM);
+        test("1000000null", "" + sf_I + sf_oAN);
+        test("100000025000000", "" + sf_I + s_I);
+        test("null-96.0", "" + f_oNtS + s_dM);
+        test("nullnull", "" + f_oNtS + s_oNtS);
+        test("null\u045176", "" + f_oNtS + f_strU);
+        test("null92", "" + f_oNtS + sf_strU2);
+        test("null51", "" + f_oNtS + sf_strU1);
+        test("nullnull", "" + f_oNtS + s_iAN);
+        test("null-54", "" + f_oNtS + f_bM);
+        test("null-87.0", "" + f_oNtS + f_fM);
+        test("nullnull", "" + f_oNtS + s_oAN);
+        test("null19", "" + f_oNtS + f_str);
+        test("null-41", "" + f_oNtS + sf_bM);
+        test("nullnull", "" + f_oNtS + sf_IN);
+        test("nullT", "" + f_oNtS + s_c);
+        test("null-42.0", "" + f_oNtS + sf_fM);
+        test("null25", "" + f_oNtS + s_b);
+        test("nullnull", "" + f_oNtS + f_oN);
+        test("null-1410065408", "" + f_oNtS + s_lM);
+        test("null8.0", "" + f_oNtS + s_d);
+        test("null55.0", "" + f_oNtS + s_f);
+        test("null97000000", "" + f_oNtS + s_i);
+        test("null-9900", "" + f_oNtS + f_sM);
+        test("null935228928", "" + f_oNtS + s_l);
+        test("null-8400", "" + f_oNtS + sf_sM);
+        test("nullC(82)", "" + f_oNtS + s_o);
+        test("nullnull", "" + f_oNtS + sf_oNtS);
+        test("nulltrue", "" + f_oNtS + s_bl);
+        test("null3900", "" + f_oNtS + s_s);
+        test("nullnull", "" + f_oNtS + sf_oN);
+        test("null94000000", "" + f_oNtS + f_I);
+        test("nullnull", "" + f_oNtS + f_IN);
+        test("nulltrue", "" + f_oNtS + sf_bl);
+        test("null5500", "" + f_oNtS + sf_s);
+        test("null-2900", "" + f_oNtS + s_sM);
+        test("null-194313216", "" + f_oNtS + sf_l);
+        test("null12", "" + f_oNtS + s_strU1);
+        test("nullC(87)", "" + f_oNtS + sf_o);
+        test("null91", "" + f_oNtS + s_strU2);
+        test("null21", "" + f_oNtS + f_strU1);
+        test("null18", "" + f_oNtS + f_strU2);
+        test("nullnull", "" + f_oNtS + f_iAN);
+        test("nullnull", "" + f_oNtS + s_oN);
+        test("null\u045180", "" + f_oNtS + s_strU);
+        test("nullC", "" + f_oNtS + sf_c);
+        test("null75", "" + f_oNtS + sf_str);
+        test("null-43", "" + f_oNtS + s_bM);
+        test("null80", "" + f_oNtS + sf_b);
+        test("nullnull", "" + f_oNtS + s_IN);
+        test("null-52.0", "" + f_oNtS + s_fM);
+        test("null75000000", "" + f_oNtS + sf_i);
+        test("null44", "" + f_oNtS + f_b);
+        test("null-1705032704", "" + f_oNtS + sf_lM);
+        test("nullnull", "" + f_oNtS + f_oAN);
+        test("null83.0", "" + f_oNtS + f_d);
+        test("nullI", "" + f_oNtS + f_c);
+        test("null94.0", "" + f_oNtS + f_f);
+        test("null12.0", "" + f_oNtS + sf_d);
+        test("null-99.0", "" + f_oNtS + f_dM);
+        test("null17.0", "" + f_oNtS + sf_f);
+        test("null-84.0", "" + f_oNtS + sf_dM);
+        test("null58000000", "" + f_oNtS + f_i);
+        test("null-55000000", "" + f_oNtS + f_iM);
+        test("null1460392448", "" + f_oNtS + f_l);
+        test("nullC(70)", "" + f_oNtS + f_o);
+        test("null\u04511", "" + f_oNtS + sf_strU);
+        test("null8000", "" + f_oNtS + f_s);
+        test("null18", "" + f_oNtS + s_str);
+        test("null-1000000", "" + f_oNtS + s_iM);
+        test("null1000000", "" + f_oNtS + sf_I);
+        test("nullnull", "" + f_oNtS + f_oNtS);
+        test("nullfalse", "" + f_oNtS + f_bl);
+        test("nullnull", "" + f_oNtS + sf_iAN);
+        test("null-2000000", "" + f_oNtS + sf_iM);
+        test("null-820130816", "" + f_oNtS + f_lM);
+        test("nullnull", "" + f_oNtS + sf_oAN);
+        test("null25000000", "" + f_oNtS + s_I);
+        test("false-96.0", "" + f_bl + s_dM);
+        test("falsenull", "" + f_bl + s_oNtS);
+        test("false\u045176", "" + f_bl + f_strU);
+        test("false92", "" + f_bl + sf_strU2);
+        test("false51", "" + f_bl + sf_strU1);
+        test("falsenull", "" + f_bl + s_iAN);
+        test("false-54", "" + f_bl + f_bM);
+        test("false-87.0", "" + f_bl + f_fM);
+        test("falsenull", "" + f_bl + s_oAN);
+        test("false19", "" + f_bl + f_str);
+        test("false-41", "" + f_bl + sf_bM);
+        test("falsenull", "" + f_bl + sf_IN);
+        test("falseT", "" + f_bl + s_c);
+        test("false-42.0", "" + f_bl + sf_fM);
+        test("false25", "" + f_bl + s_b);
+        test("falsenull", "" + f_bl + f_oN);
+        test("false-1410065408", "" + f_bl + s_lM);
+        test("false8.0", "" + f_bl + s_d);
+        test("false55.0", "" + f_bl + s_f);
+        test("false97000000", "" + f_bl + s_i);
+        test("false-9900", "" + f_bl + f_sM);
+        test("false935228928", "" + f_bl + s_l);
+        test("false-8400", "" + f_bl + sf_sM);
+        test("falseC(82)", "" + f_bl + s_o);
+        test("falsenull", "" + f_bl + sf_oNtS);
+        test("falsetrue", "" + f_bl + s_bl);
+        test("false3900", "" + f_bl + s_s);
+        test("falsenull", "" + f_bl + sf_oN);
+        test("false94000000", "" + f_bl + f_I);
+        test("falsenull", "" + f_bl + f_IN);
+        test("falsetrue", "" + f_bl + sf_bl);
+        test("false5500", "" + f_bl + sf_s);
+        test("false-2900", "" + f_bl + s_sM);
+        test("false-194313216", "" + f_bl + sf_l);
+        test("false12", "" + f_bl + s_strU1);
+        test("falseC(87)", "" + f_bl + sf_o);
+        test("false91", "" + f_bl + s_strU2);
+        test("false21", "" + f_bl + f_strU1);
+        test("false18", "" + f_bl + f_strU2);
+        test("falsenull", "" + f_bl + f_iAN);
+        test("falsenull", "" + f_bl + s_oN);
+        test("false\u045180", "" + f_bl + s_strU);
+        test("falseC", "" + f_bl + sf_c);
+        test("false75", "" + f_bl + sf_str);
+        test("false-43", "" + f_bl + s_bM);
+        test("false80", "" + f_bl + sf_b);
+        test("falsenull", "" + f_bl + s_IN);
+        test("false-52.0", "" + f_bl + s_fM);
+        test("false75000000", "" + f_bl + sf_i);
+        test("false44", "" + f_bl + f_b);
+        test("false-1705032704", "" + f_bl + sf_lM);
+        test("falsenull", "" + f_bl + f_oAN);
+        test("false83.0", "" + f_bl + f_d);
+        test("falseI", "" + f_bl + f_c);
+        test("false94.0", "" + f_bl + f_f);
+        test("false12.0", "" + f_bl + sf_d);
+        test("false-99.0", "" + f_bl + f_dM);
+        test("false17.0", "" + f_bl + sf_f);
+        test("false-84.0", "" + f_bl + sf_dM);
+        test("false58000000", "" + f_bl + f_i);
+        test("false-55000000", "" + f_bl + f_iM);
+        test("false1460392448", "" + f_bl + f_l);
+        test("falseC(70)", "" + f_bl + f_o);
+        test("false\u04511", "" + f_bl + sf_strU);
+        test("false8000", "" + f_bl + f_s);
+        test("false18", "" + f_bl + s_str);
+        test("false-1000000", "" + f_bl + s_iM);
+        test("false1000000", "" + f_bl + sf_I);
+        test("falsenull", "" + f_bl + f_oNtS);
+        test("falsefalse", "" + f_bl + f_bl);
+        test("falsenull", "" + f_bl + sf_iAN);
+        test("false-2000000", "" + f_bl + sf_iM);
+        test("false-820130816", "" + f_bl + f_lM);
+        test("falsenull", "" + f_bl + sf_oAN);
+        test("false25000000", "" + f_bl + s_I);
+        test("null-96.0", "" + sf_iAN + s_dM);
+        test("nullnull", "" + sf_iAN + s_oNtS);
+        test("null\u045176", "" + sf_iAN + f_strU);
+        test("null92", "" + sf_iAN + sf_strU2);
+        test("null51", "" + sf_iAN + sf_strU1);
+        test("nullnull", "" + sf_iAN + s_iAN);
+        test("null-54", "" + sf_iAN + f_bM);
+        test("null-87.0", "" + sf_iAN + f_fM);
+        test("nullnull", "" + sf_iAN + s_oAN);
+        test("null19", "" + sf_iAN + f_str);
+        test("null-41", "" + sf_iAN + sf_bM);
+        test("nullnull", "" + sf_iAN + sf_IN);
+        test("nullT", "" + sf_iAN + s_c);
+        test("null-42.0", "" + sf_iAN + sf_fM);
+        test("null25", "" + sf_iAN + s_b);
+        test("nullnull", "" + sf_iAN + f_oN);
+        test("null-1410065408", "" + sf_iAN + s_lM);
+        test("null8.0", "" + sf_iAN + s_d);
+        test("null55.0", "" + sf_iAN + s_f);
+        test("null97000000", "" + sf_iAN + s_i);
+        test("null-9900", "" + sf_iAN + f_sM);
+        test("null935228928", "" + sf_iAN + s_l);
+        test("null-8400", "" + sf_iAN + sf_sM);
+        test("nullC(82)", "" + sf_iAN + s_o);
+        test("nullnull", "" + sf_iAN + sf_oNtS);
+        test("nulltrue", "" + sf_iAN + s_bl);
+        test("null3900", "" + sf_iAN + s_s);
+        test("nullnull", "" + sf_iAN + sf_oN);
+        test("null94000000", "" + sf_iAN + f_I);
+        test("nullnull", "" + sf_iAN + f_IN);
+        test("nulltrue", "" + sf_iAN + sf_bl);
+        test("null5500", "" + sf_iAN + sf_s);
+        test("null-2900", "" + sf_iAN + s_sM);
+        test("null-194313216", "" + sf_iAN + sf_l);
+        test("null12", "" + sf_iAN + s_strU1);
+        test("nullC(87)", "" + sf_iAN + sf_o);
+        test("null91", "" + sf_iAN + s_strU2);
+        test("null21", "" + sf_iAN + f_strU1);
+        test("null18", "" + sf_iAN + f_strU2);
+        test("nullnull", "" + sf_iAN + f_iAN);
+        test("nullnull", "" + sf_iAN + s_oN);
+        test("null\u045180", "" + sf_iAN + s_strU);
+        test("nullC", "" + sf_iAN + sf_c);
+        test("null75", "" + sf_iAN + sf_str);
+        test("null-43", "" + sf_iAN + s_bM);
+        test("null80", "" + sf_iAN + sf_b);
+        test("nullnull", "" + sf_iAN + s_IN);
+        test("null-52.0", "" + sf_iAN + s_fM);
+        test("null75000000", "" + sf_iAN + sf_i);
+        test("null44", "" + sf_iAN + f_b);
+        test("null-1705032704", "" + sf_iAN + sf_lM);
+        test("nullnull", "" + sf_iAN + f_oAN);
+        test("null83.0", "" + sf_iAN + f_d);
+        test("nullI", "" + sf_iAN + f_c);
+        test("null94.0", "" + sf_iAN + f_f);
+        test("null12.0", "" + sf_iAN + sf_d);
+        test("null-99.0", "" + sf_iAN + f_dM);
+        test("null17.0", "" + sf_iAN + sf_f);
+        test("null-84.0", "" + sf_iAN + sf_dM);
+        test("null58000000", "" + sf_iAN + f_i);
+        test("null-55000000", "" + sf_iAN + f_iM);
+        test("null1460392448", "" + sf_iAN + f_l);
+        test("nullC(70)", "" + sf_iAN + f_o);
+        test("null\u04511", "" + sf_iAN + sf_strU);
+        test("null8000", "" + sf_iAN + f_s);
+        test("null18", "" + sf_iAN + s_str);
+        test("null-1000000", "" + sf_iAN + s_iM);
+        test("null1000000", "" + sf_iAN + sf_I);
+        test("nullnull", "" + sf_iAN + f_oNtS);
+        test("nullfalse", "" + sf_iAN + f_bl);
+        test("nullnull", "" + sf_iAN + sf_iAN);
+        test("null-2000000", "" + sf_iAN + sf_iM);
+        test("null-820130816", "" + sf_iAN + f_lM);
+        test("nullnull", "" + sf_iAN + sf_oAN);
+        test("null25000000", "" + sf_iAN + s_I);
+        test("-2000000-96.0", "" + sf_iM + s_dM);
+        test("-2000000null", "" + sf_iM + s_oNtS);
+        test("-2000000\u045176", "" + sf_iM + f_strU);
+        test("-200000092", "" + sf_iM + sf_strU2);
+        test("-200000051", "" + sf_iM + sf_strU1);
+        test("-2000000null", "" + sf_iM + s_iAN);
+        test("-2000000-54", "" + sf_iM + f_bM);
+        test("-2000000-87.0", "" + sf_iM + f_fM);
+        test("-2000000null", "" + sf_iM + s_oAN);
+        test("-200000019", "" + sf_iM + f_str);
+        test("-2000000-41", "" + sf_iM + sf_bM);
+        test("-2000000null", "" + sf_iM + sf_IN);
+        test("-2000000T", "" + sf_iM + s_c);
+        test("-2000000-42.0", "" + sf_iM + sf_fM);
+        test("-200000025", "" + sf_iM + s_b);
+        test("-2000000null", "" + sf_iM + f_oN);
+        test("-2000000-1410065408", "" + sf_iM + s_lM);
+        test("-20000008.0", "" + sf_iM + s_d);
+        test("-200000055.0", "" + sf_iM + s_f);
+        test("-200000097000000", "" + sf_iM + s_i);
+        test("-2000000-9900", "" + sf_iM + f_sM);
+        test("-2000000935228928", "" + sf_iM + s_l);
+        test("-2000000-8400", "" + sf_iM + sf_sM);
+        test("-2000000C(82)", "" + sf_iM + s_o);
+        test("-2000000null", "" + sf_iM + sf_oNtS);
+        test("-2000000true", "" + sf_iM + s_bl);
+        test("-20000003900", "" + sf_iM + s_s);
+        test("-2000000null", "" + sf_iM + sf_oN);
+        test("-200000094000000", "" + sf_iM + f_I);
+        test("-2000000null", "" + sf_iM + f_IN);
+        test("-2000000true", "" + sf_iM + sf_bl);
+        test("-20000005500", "" + sf_iM + sf_s);
+        test("-2000000-2900", "" + sf_iM + s_sM);
+        test("-2000000-194313216", "" + sf_iM + sf_l);
+        test("-200000012", "" + sf_iM + s_strU1);
+        test("-2000000C(87)", "" + sf_iM + sf_o);
+        test("-200000091", "" + sf_iM + s_strU2);
+        test("-200000021", "" + sf_iM + f_strU1);
+        test("-200000018", "" + sf_iM + f_strU2);
+        test("-2000000null", "" + sf_iM + f_iAN);
+        test("-2000000null", "" + sf_iM + s_oN);
+        test("-2000000\u045180", "" + sf_iM + s_strU);
+        test("-2000000C", "" + sf_iM + sf_c);
+        test("-200000075", "" + sf_iM + sf_str);
+        test("-2000000-43", "" + sf_iM + s_bM);
+        test("-200000080", "" + sf_iM + sf_b);
+        test("-2000000null", "" + sf_iM + s_IN);
+        test("-2000000-52.0", "" + sf_iM + s_fM);
+        test("-200000075000000", "" + sf_iM + sf_i);
+        test("-200000044", "" + sf_iM + f_b);
+        test("-2000000-1705032704", "" + sf_iM + sf_lM);
+        test("-2000000null", "" + sf_iM + f_oAN);
+        test("-200000083.0", "" + sf_iM + f_d);
+        test("-2000000I", "" + sf_iM + f_c);
+        test("-200000094.0", "" + sf_iM + f_f);
+        test("-200000012.0", "" + sf_iM + sf_d);
+        test("-2000000-99.0", "" + sf_iM + f_dM);
+        test("-200000017.0", "" + sf_iM + sf_f);
+        test("-2000000-84.0", "" + sf_iM + sf_dM);
+        test("-200000058000000", "" + sf_iM + f_i);
+        test("-2000000-55000000", "" + sf_iM + f_iM);
+        test("-20000001460392448", "" + sf_iM + f_l);
+        test("-2000000C(70)", "" + sf_iM + f_o);
+        test("-2000000\u04511", "" + sf_iM + sf_strU);
+        test("-20000008000", "" + sf_iM + f_s);
+        test("-200000018", "" + sf_iM + s_str);
+        test("-2000000-1000000", "" + sf_iM + s_iM);
+        test("-20000001000000", "" + sf_iM + sf_I);
+        test("-2000000null", "" + sf_iM + f_oNtS);
+        test("-2000000false", "" + sf_iM + f_bl);
+        test("-2000000null", "" + sf_iM + sf_iAN);
+        test("-2000000-2000000", "" + sf_iM + sf_iM);
+        test("-2000000-820130816", "" + sf_iM + f_lM);
+        test("-2000000null", "" + sf_iM + sf_oAN);
+        test("-200000025000000", "" + sf_iM + s_I);
+        test("-820130816-96.0", "" + f_lM + s_dM);
+        test("-820130816null", "" + f_lM + s_oNtS);
+        test("-820130816\u045176", "" + f_lM + f_strU);
+        test("-82013081692", "" + f_lM + sf_strU2);
+        test("-82013081651", "" + f_lM + sf_strU1);
+        test("-820130816null", "" + f_lM + s_iAN);
+        test("-820130816-54", "" + f_lM + f_bM);
+        test("-820130816-87.0", "" + f_lM + f_fM);
+        test("-820130816null", "" + f_lM + s_oAN);
+        test("-82013081619", "" + f_lM + f_str);
+        test("-820130816-41", "" + f_lM + sf_bM);
+        test("-820130816null", "" + f_lM + sf_IN);
+        test("-820130816T", "" + f_lM + s_c);
+        test("-820130816-42.0", "" + f_lM + sf_fM);
+        test("-82013081625", "" + f_lM + s_b);
+        test("-820130816null", "" + f_lM + f_oN);
+        test("-820130816-1410065408", "" + f_lM + s_lM);
+        test("-8201308168.0", "" + f_lM + s_d);
+        test("-82013081655.0", "" + f_lM + s_f);
+        test("-82013081697000000", "" + f_lM + s_i);
+        test("-820130816-9900", "" + f_lM + f_sM);
+        test("-820130816935228928", "" + f_lM + s_l);
+        test("-820130816-8400", "" + f_lM + sf_sM);
+        test("-820130816C(82)", "" + f_lM + s_o);
+        test("-820130816null", "" + f_lM + sf_oNtS);
+        test("-820130816true", "" + f_lM + s_bl);
+        test("-8201308163900", "" + f_lM + s_s);
+        test("-820130816null", "" + f_lM + sf_oN);
+        test("-82013081694000000", "" + f_lM + f_I);
+        test("-820130816null", "" + f_lM + f_IN);
+        test("-820130816true", "" + f_lM + sf_bl);
+        test("-8201308165500", "" + f_lM + sf_s);
+        test("-820130816-2900", "" + f_lM + s_sM);
+        test("-820130816-194313216", "" + f_lM + sf_l);
+        test("-82013081612", "" + f_lM + s_strU1);
+        test("-820130816C(87)", "" + f_lM + sf_o);
+        test("-82013081691", "" + f_lM + s_strU2);
+        test("-82013081621", "" + f_lM + f_strU1);
+        test("-82013081618", "" + f_lM + f_strU2);
+        test("-820130816null", "" + f_lM + f_iAN);
+        test("-820130816null", "" + f_lM + s_oN);
+        test("-820130816\u045180", "" + f_lM + s_strU);
+        test("-820130816C", "" + f_lM + sf_c);
+        test("-82013081675", "" + f_lM + sf_str);
+        test("-820130816-43", "" + f_lM + s_bM);
+        test("-82013081680", "" + f_lM + sf_b);
+        test("-820130816null", "" + f_lM + s_IN);
+        test("-820130816-52.0", "" + f_lM + s_fM);
+        test("-82013081675000000", "" + f_lM + sf_i);
+        test("-82013081644", "" + f_lM + f_b);
+        test("-820130816-1705032704", "" + f_lM + sf_lM);
+        test("-820130816null", "" + f_lM + f_oAN);
+        test("-82013081683.0", "" + f_lM + f_d);
+        test("-820130816I", "" + f_lM + f_c);
+        test("-82013081694.0", "" + f_lM + f_f);
+        test("-82013081612.0", "" + f_lM + sf_d);
+        test("-820130816-99.0", "" + f_lM + f_dM);
+        test("-82013081617.0", "" + f_lM + sf_f);
+        test("-820130816-84.0", "" + f_lM + sf_dM);
+        test("-82013081658000000", "" + f_lM + f_i);
+        test("-820130816-55000000", "" + f_lM + f_iM);
+        test("-8201308161460392448", "" + f_lM + f_l);
+        test("-820130816C(70)", "" + f_lM + f_o);
+        test("-820130816\u04511", "" + f_lM + sf_strU);
+        test("-8201308168000", "" + f_lM + f_s);
+        test("-82013081618", "" + f_lM + s_str);
+        test("-820130816-1000000", "" + f_lM + s_iM);
+        test("-8201308161000000", "" + f_lM + sf_I);
+        test("-820130816null", "" + f_lM + f_oNtS);
+        test("-820130816false", "" + f_lM + f_bl);
+        test("-820130816null", "" + f_lM + sf_iAN);
+        test("-820130816-2000000", "" + f_lM + sf_iM);
+        test("-820130816-820130816", "" + f_lM + f_lM);
+        test("-820130816null", "" + f_lM + sf_oAN);
+        test("-82013081625000000", "" + f_lM + s_I);
+        test("null-96.0", "" + sf_oAN + s_dM);
+        test("nullnull", "" + sf_oAN + s_oNtS);
+        test("null\u045176", "" + sf_oAN + f_strU);
+        test("null92", "" + sf_oAN + sf_strU2);
+        test("null51", "" + sf_oAN + sf_strU1);
+        test("nullnull", "" + sf_oAN + s_iAN);
+        test("null-54", "" + sf_oAN + f_bM);
+        test("null-87.0", "" + sf_oAN + f_fM);
+        test("nullnull", "" + sf_oAN + s_oAN);
+        test("null19", "" + sf_oAN + f_str);
+        test("null-41", "" + sf_oAN + sf_bM);
+        test("nullnull", "" + sf_oAN + sf_IN);
+        test("nullT", "" + sf_oAN + s_c);
+        test("null-42.0", "" + sf_oAN + sf_fM);
+        test("null25", "" + sf_oAN + s_b);
+        test("nullnull", "" + sf_oAN + f_oN);
+        test("null-1410065408", "" + sf_oAN + s_lM);
+        test("null8.0", "" + sf_oAN + s_d);
+        test("null55.0", "" + sf_oAN + s_f);
+        test("null97000000", "" + sf_oAN + s_i);
+        test("null-9900", "" + sf_oAN + f_sM);
+        test("null935228928", "" + sf_oAN + s_l);
+        test("null-8400", "" + sf_oAN + sf_sM);
+        test("nullC(82)", "" + sf_oAN + s_o);
+        test("nullnull", "" + sf_oAN + sf_oNtS);
+        test("nulltrue", "" + sf_oAN + s_bl);
+        test("null3900", "" + sf_oAN + s_s);
+        test("nullnull", "" + sf_oAN + sf_oN);
+        test("null94000000", "" + sf_oAN + f_I);
+        test("nullnull", "" + sf_oAN + f_IN);
+        test("nulltrue", "" + sf_oAN + sf_bl);
+        test("null5500", "" + sf_oAN + sf_s);
+        test("null-2900", "" + sf_oAN + s_sM);
+        test("null-194313216", "" + sf_oAN + sf_l);
+        test("null12", "" + sf_oAN + s_strU1);
+        test("nullC(87)", "" + sf_oAN + sf_o);
+        test("null91", "" + sf_oAN + s_strU2);
+        test("null21", "" + sf_oAN + f_strU1);
+        test("null18", "" + sf_oAN + f_strU2);
+        test("nullnull", "" + sf_oAN + f_iAN);
+        test("nullnull", "" + sf_oAN + s_oN);
+        test("null\u045180", "" + sf_oAN + s_strU);
+        test("nullC", "" + sf_oAN + sf_c);
+        test("null75", "" + sf_oAN + sf_str);
+        test("null-43", "" + sf_oAN + s_bM);
+        test("null80", "" + sf_oAN + sf_b);
+        test("nullnull", "" + sf_oAN + s_IN);
+        test("null-52.0", "" + sf_oAN + s_fM);
+        test("null75000000", "" + sf_oAN + sf_i);
+        test("null44", "" + sf_oAN + f_b);
+        test("null-1705032704", "" + sf_oAN + sf_lM);
+        test("nullnull", "" + sf_oAN + f_oAN);
+        test("null83.0", "" + sf_oAN + f_d);
+        test("nullI", "" + sf_oAN + f_c);
+        test("null94.0", "" + sf_oAN + f_f);
+        test("null12.0", "" + sf_oAN + sf_d);
+        test("null-99.0", "" + sf_oAN + f_dM);
+        test("null17.0", "" + sf_oAN + sf_f);
+        test("null-84.0", "" + sf_oAN + sf_dM);
+        test("null58000000", "" + sf_oAN + f_i);
+        test("null-55000000", "" + sf_oAN + f_iM);
+        test("null1460392448", "" + sf_oAN + f_l);
+        test("nullC(70)", "" + sf_oAN + f_o);
+        test("null\u04511", "" + sf_oAN + sf_strU);
+        test("null8000", "" + sf_oAN + f_s);
+        test("null18", "" + sf_oAN + s_str);
+        test("null-1000000", "" + sf_oAN + s_iM);
+        test("null1000000", "" + sf_oAN + sf_I);
+        test("nullnull", "" + sf_oAN + f_oNtS);
+        test("nullfalse", "" + sf_oAN + f_bl);
+        test("nullnull", "" + sf_oAN + sf_iAN);
+        test("null-2000000", "" + sf_oAN + sf_iM);
+        test("null-820130816", "" + sf_oAN + f_lM);
+        test("nullnull", "" + sf_oAN + sf_oAN);
+        test("null25000000", "" + sf_oAN + s_I);
+        test("25000000-96.0", "" + s_I + s_dM);
+        test("25000000null", "" + s_I + s_oNtS);
+        test("25000000\u045176", "" + s_I + f_strU);
+        test("2500000092", "" + s_I + sf_strU2);
+        test("2500000051", "" + s_I + sf_strU1);
+        test("25000000null", "" + s_I + s_iAN);
+        test("25000000-54", "" + s_I + f_bM);
+        test("25000000-87.0", "" + s_I + f_fM);
+        test("25000000null", "" + s_I + s_oAN);
+        test("2500000019", "" + s_I + f_str);
+        test("25000000-41", "" + s_I + sf_bM);
+        test("25000000null", "" + s_I + sf_IN);
+        test("25000000T", "" + s_I + s_c);
+        test("25000000-42.0", "" + s_I + sf_fM);
+        test("2500000025", "" + s_I + s_b);
+        test("25000000null", "" + s_I + f_oN);
+        test("25000000-1410065408", "" + s_I + s_lM);
+        test("250000008.0", "" + s_I + s_d);
+        test("2500000055.0", "" + s_I + s_f);
+        test("2500000097000000", "" + s_I + s_i);
+        test("25000000-9900", "" + s_I + f_sM);
+        test("25000000935228928", "" + s_I + s_l);
+        test("25000000-8400", "" + s_I + sf_sM);
+        test("25000000C(82)", "" + s_I + s_o);
+        test("25000000null", "" + s_I + sf_oNtS);
+        test("25000000true", "" + s_I + s_bl);
+        test("250000003900", "" + s_I + s_s);
+        test("25000000null", "" + s_I + sf_oN);
+        test("2500000094000000", "" + s_I + f_I);
+        test("25000000null", "" + s_I + f_IN);
+        test("25000000true", "" + s_I + sf_bl);
+        test("250000005500", "" + s_I + sf_s);
+        test("25000000-2900", "" + s_I + s_sM);
+        test("25000000-194313216", "" + s_I + sf_l);
+        test("2500000012", "" + s_I + s_strU1);
+        test("25000000C(87)", "" + s_I + sf_o);
+        test("2500000091", "" + s_I + s_strU2);
+        test("2500000021", "" + s_I + f_strU1);
+        test("2500000018", "" + s_I + f_strU2);
+        test("25000000null", "" + s_I + f_iAN);
+        test("25000000null", "" + s_I + s_oN);
+        test("25000000\u045180", "" + s_I + s_strU);
+        test("25000000C", "" + s_I + sf_c);
+        test("2500000075", "" + s_I + sf_str);
+        test("25000000-43", "" + s_I + s_bM);
+        test("2500000080", "" + s_I + sf_b);
+        test("25000000null", "" + s_I + s_IN);
+        test("25000000-52.0", "" + s_I + s_fM);
+        test("2500000075000000", "" + s_I + sf_i);
+        test("2500000044", "" + s_I + f_b);
+        test("25000000-1705032704", "" + s_I + sf_lM);
+        test("25000000null", "" + s_I + f_oAN);
+        test("2500000083.0", "" + s_I + f_d);
+        test("25000000I", "" + s_I + f_c);
+        test("2500000094.0", "" + s_I + f_f);
+        test("2500000012.0", "" + s_I + sf_d);
+        test("25000000-99.0", "" + s_I + f_dM);
+        test("2500000017.0", "" + s_I + sf_f);
+        test("25000000-84.0", "" + s_I + sf_dM);
+        test("2500000058000000", "" + s_I + f_i);
+        test("25000000-55000000", "" + s_I + f_iM);
+        test("250000001460392448", "" + s_I + f_l);
+        test("25000000C(70)", "" + s_I + f_o);
+        test("25000000\u04511", "" + s_I + sf_strU);
+        test("250000008000", "" + s_I + f_s);
+        test("2500000018", "" + s_I + s_str);
+        test("25000000-1000000", "" + s_I + s_iM);
+        test("250000001000000", "" + s_I + sf_I);
+        test("25000000null", "" + s_I + f_oNtS);
+        test("25000000false", "" + s_I + f_bl);
+        test("25000000null", "" + s_I + sf_iAN);
+        test("25000000-2000000", "" + s_I + sf_iM);
+        test("25000000-820130816", "" + s_I + f_lM);
+        test("25000000null", "" + s_I + sf_oAN);
+        test("2500000025000000", "" + s_I + s_I);
+    }
+
+}
diff --git a/jdk/test/java/lang/String/concat/ImplicitStringConcatShapesTestGen.java b/jdk/test/java/lang/String/concat/ImplicitStringConcatShapesTestGen.java
new file mode 100644
index 00000000000..8b42e4607e4
--- /dev/null
+++ b/jdk/test/java/lang/String/concat/ImplicitStringConcatShapesTestGen.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.*;
+
+public class ImplicitStringConcatShapesTestGen {
+    public static String escapeToUnicode(String str) {
+        StringBuilder b = new StringBuilder();
+        for (char c : str.toCharArray()) {
+            if (c < 128) {
+                b.append(c);
+            } else {
+                b.append("\\u").append(String.format("%04X", (int) c));
+            }
+        }
+        return b.toString();
+    }
+
+    public static void main(String... args) throws IOException {
+        PrintWriter pw = new PrintWriter(System.out);
+
+        String[] types = {
+                "boolean",
+                "byte",
+                "byteMinus",
+                "char",
+                "short",
+                "shortMinus",
+                "int",
+                "intMinus",
+                "integer",
+                "integerNull",
+                "float",
+                "floatMinus",
+                "long",
+                "longMinus",
+                "double",
+                "doubleMinus",
+                "object",
+                "objectNull",
+                "objectNullToString",
+                "String",
+                "StringUTF16",
+                "StringU1",
+                "StringU2",
+                "intArrayNull",
+                "objectArrayNull",
+        };
+
+        for (String t : Files.readAllLines(Paths.get("ImplicitStringConcatShapes-head.template"))) {
+            pw.println(t);
+        }
+
+        Map<String, String> values = new HashMap<>();
+
+        Random current = new Random(12345);
+        for (int mode = 0; mode <= 2; mode++) {
+            for (String type : types) {
+                int i = current.nextInt(100);
+                boolean isStatic = (mode | 1) == 1;
+                boolean isFinal = (mode | 2) == 2;
+                String fieldName = (isStatic ? "s" : "") + (isFinal ? "f" : "") + "_" + typeSig(type);
+                String value = initValue(type, i);
+                String stringValue = stringValue(type, i);
+                values.put(fieldName, stringValue);
+                pw.printf("    %s %s %s %s = %s;%n", isStatic ? "static" : "", isFinal ? "final" : "", typeValue(type, i), fieldName, value);
+            }
+        }
+
+        pw.println();
+
+        List<String> lines = new ArrayList<>();
+        List<String> l = new ArrayList<>(values.keySet());
+
+        for (String l1 : l) {
+            lines.add(String.format("test(\"%s\", \"\" + %s);",
+                    escapeToUnicode(values.get(l1)),
+                    l1
+            ));
+        }
+
+        for (String l1 : l) {
+            for (String l2 : l) {
+                lines.add(String.format("test(\"%s\", \"\" + %s + %s);",
+                        escapeToUnicode(values.get(l1) + values.get(l2)),
+                        l1, l2
+                ));
+            }
+        }
+
+        final int STRIDE = 1000;
+        int strides = lines.size() / STRIDE + 1;
+
+        pw.println("    public void run() {");
+        for (int c = 0; c < strides; c++) {
+            pw.println("        run" + c + "();");
+        }
+        pw.println("    }");
+        pw.println();
+
+        for (int c = 0; c < strides; c++) {
+            pw.println("    public void run" + c + "() {");
+            for (String line : lines.subList(c * STRIDE, Math.min(lines.size(), (c+1) * STRIDE))) {
+                pw.println("        " + line);
+            }
+            pw.println("    }");
+            pw.println();
+        }
+
+        pw.println("}");
+
+        pw.flush();
+        pw.close();
+    }
+
+    private static String typeSig(String type) {
+        switch (type) {
+            case "boolean":             return "bl";
+            case "byte":                return "b";
+            case "byteMinus":           return "bM";
+            case "short":               return "s";
+            case "shortMinus":          return "sM";
+            case "char":                return "c";
+            case "int":                 return "i";
+            case "intMinus":            return "iM";
+            case "integer":             return "I";
+            case "integerNull":         return "IN";
+            case "float":               return "f";
+            case "floatMinus":          return "fM";
+            case "long":                return "l";
+            case "longMinus":           return "lM";
+            case "double":              return "d";
+            case "doubleMinus":         return "dM";
+            case "String":              return "str";
+            case "StringUTF16":         return "strU";
+            case "StringU1":            return "strU1";
+            case "StringU2":            return "strU2";
+            case "object":              return "o";
+            case "objectNull":          return "oN";
+            case "objectNullToString":  return "oNtS";
+            case "intArrayNull":        return "iAN";
+            case "objectArrayNull":     return "oAN";
+            default:
+                throw new IllegalStateException();
+        }
+    }
+
+    private static String typeValue(String type, int i) {
+        switch (type) {
+            case "boolean":
+            case "byte":
+            case "byteMinus":
+            case "char":
+            case "short":
+            case "shortMinus":
+            case "int":
+            case "intMinus":
+            case "float":
+            case "floatMinus":
+            case "long":
+            case "longMinus":
+            case "double":
+            case "doubleMinus":
+                return type.replace("Minus", "");
+            case "String":
+            case "StringUTF16":
+            case "StringU1":
+            case "StringU2":
+                return "String";
+            case "object":
+            case "objectNull":
+            case "objectNullToString":
+                return "Object";
+            case "integer":
+            case "integerNull":
+                return "Integer";
+            case "intArrayNull":
+                return "int[]";
+            case "objectArrayNull":
+                return "Object[]";
+            default:
+                throw new IllegalStateException();
+        }
+    }
+
+    private static String initValue(String type, int i) {
+        switch (type) {
+            case "boolean":
+                return String.valueOf((i & 1) == 1);
+            case "byte":
+                return String.valueOf(i);
+            case "byteMinus":
+                return String.valueOf(-i);
+            case "short":
+                return String.valueOf(i*100);
+            case "shortMinus":
+                return String.valueOf(-i*100);
+            case "intMinus":
+                return String.valueOf(-i*1_000_000);
+            case "int":
+            case "integer":
+                return String.valueOf(i*1_000_000);
+            case "long":
+                return String.valueOf(i*1_000_000_000) + "L";
+            case "longMinus":
+                return String.valueOf(-i*1_000_000_000) + "L";
+            case "char":
+                return "'" + (char)(i % 26 + 65) + "'";
+            case "double":
+                return String.valueOf(i) + ".0d";
+            case "doubleMinus":
+                return "-" + String.valueOf(i) + ".0d";
+            case "float":
+                return String.valueOf(i) + ".0f";
+            case "floatMinus":
+                return "-" + String.valueOf(i) + ".0f";
+            case "object":
+                return "new MyClass(" + i + ")";
+            case "objectNullToString":
+                return "new MyClassNullToString()";
+            case "integerNull":
+            case "objectNull":
+            case "intArrayNull":
+            case "objectArrayNull":
+                return "null";
+            case "String":
+                return "\"" + i + "\"";
+            case "StringUTF16":
+                return "\"\\u0451" + i + "\"";
+            case "StringU1":
+                return "\"\\u0001" + i + "\"";
+            case "StringU2":
+                return "\"\\u0002" + i + "\"";
+            default:
+                throw new IllegalStateException();
+        }
+    }
+
+    private static String stringValue(String type, int i) {
+        switch (type) {
+            case "boolean":
+                return String.valueOf((i & 1) == 1);
+            case "byte":
+                return String.valueOf(i);
+            case "byteMinus":
+                return String.valueOf(-i);
+            case "short":
+                return String.valueOf(i*100);
+            case "shortMinus":
+                return String.valueOf(-i*100);
+            case "intMinus":
+                return String.valueOf(-i*1_000_000);
+            case "int":
+            case "integer":
+                return String.valueOf(i*1_000_000);
+            case "long":
+                return String.valueOf(i*1_000_000_000);
+            case "longMinus":
+                return String.valueOf(-i*1_000_000_000);
+            case "char":
+                return String.valueOf((char) (i % 26 + 65));
+            case "double":
+            case "float":
+                return String.valueOf(i) + ".0";
+            case "doubleMinus":
+            case "floatMinus":
+                return "-" + String.valueOf(i) + ".0";
+            case "object":
+                return "C(" + i + ")";
+            case "integerNull":
+            case "objectNull":
+            case "objectNullToString":
+            case "intArrayNull":
+            case "objectArrayNull":
+                return "null";
+            case "String":
+                return "" + i;
+            case "StringUTF16":
+                return "\u0451" + i;
+            case "StringU1":
+                return "\u0001" + i;
+            case "StringU2":
+                return "\u0002" + i;
+            default:
+                throw new IllegalStateException();
+        }
+    }
+
+}
diff --git a/jdk/test/java/lang/String/concat/StringConcatFactoryInvariants.java b/jdk/test/java/lang/String/concat/StringConcatFactoryInvariants.java
new file mode 100644
index 00000000000..e1a3e8085c6
--- /dev/null
+++ b/jdk/test/java/lang/String/concat/StringConcatFactoryInvariants.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.Serializable;
+import java.lang.invoke.*;
+import java.util.concurrent.Callable;
+
+/**
+ * @test
+ * @summary Test input invariants for StringConcatFactory
+ *
+ * @compile StringConcatFactoryInvariants.java
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                                                                          StringConcatFactoryInvariants
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                                                                    StringConcatFactoryInvariants
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                                                                    StringConcatFactoryInvariants
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                                                              StringConcatFactoryInvariants
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                                                              StringConcatFactoryInvariants
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                                                                          StringConcatFactoryInvariants
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true                                              StringConcatFactoryInvariants
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true                                              StringConcatFactoryInvariants
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true                                              StringConcatFactoryInvariants
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true                                              StringConcatFactoryInvariants
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true                                              StringConcatFactoryInvariants
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true                                              StringConcatFactoryInvariants
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                                                              -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED                                                        -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT                                                  -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT                                              -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
+ *
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB                  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED            -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
+ * @run main/othervm -Djava.lang.invoke.stringConcat=BC_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_SB_SIZED_EXACT      -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
+ * @run main/othervm -Djava.lang.invoke.stringConcat=MH_INLINE_SIZED_EXACT  -Djava.lang.invoke.stringConcat.debug=true  -Djava.lang.invoke.stringConcat.cache=true  StringConcatFactoryInvariants
+ *
+*/
+public class StringConcatFactoryInvariants {
+
+    private static final char TAG_ARG   = '\u0001';
+    private static final char TAG_CONST = '\u0002';
+
+    public static void main(String[] args) throws Throwable {
+        MethodHandles.Lookup lookup = MethodHandles.publicLookup();
+        String methodName = "foo";
+        MethodType mt = MethodType.methodType(String.class, String.class, int.class);
+        String recipe = "" + TAG_ARG + TAG_ARG + TAG_CONST;
+        String[] constants = new String[]{"bar"};
+
+        final int LIMIT = 200;
+
+        // Simple factory: check for dynamic arguments overflow
+        Class<?>[] underThreshold = new Class<?>[LIMIT - 1];
+        Class<?>[] threshold      = new Class<?>[LIMIT];
+        Class<?>[] overThreshold  = new Class<?>[LIMIT + 1];
+
+        StringBuilder sbUnderThreshold = new StringBuilder();
+        sbUnderThreshold.append(TAG_CONST);
+        for (int c = 0; c < LIMIT - 1; c++) {
+            underThreshold[c] = int.class;
+            threshold[c] = int.class;
+            overThreshold[c] = int.class;
+            sbUnderThreshold.append(TAG_ARG);
+        }
+        threshold[LIMIT - 1] = int.class;
+        overThreshold[LIMIT - 1] = int.class;
+        overThreshold[LIMIT] = int.class;
+
+        String recipeEmpty = "";
+        String recipeUnderThreshold = sbUnderThreshold.toString();
+        String recipeThreshold = sbUnderThreshold.append(TAG_ARG).toString();
+        String recipeOverThreshold = sbUnderThreshold.append(TAG_ARG).toString();
+
+        MethodType mtEmpty = MethodType.methodType(String.class);
+        MethodType mtUnderThreshold = MethodType.methodType(String.class, underThreshold);
+        MethodType mtThreshold = MethodType.methodType(String.class, threshold);
+        MethodType mtOverThreshold = MethodType.methodType(String.class, overThreshold);
+
+
+        // Check the basic functionality is working
+        {
+            CallSite cs = StringConcatFactory.makeConcat(lookup, methodName, mt);
+            test("foo42", (String) cs.getTarget().invokeExact("foo", 42));
+        }
+
+        {
+            CallSite cs = StringConcatFactory.makeConcatWithConstants(lookup, methodName, mt, recipe, constants);
+            test("foo42bar", (String) cs.getTarget().invokeExact("foo", 42));
+        }
+
+        // Simple factory, check for nulls:
+        failNPE("Lookup is null",
+                () -> StringConcatFactory.makeConcat(null, methodName, mt));
+
+        failNPE("Method name is null",
+                () -> StringConcatFactory.makeConcat(lookup, null, mt));
+
+        failNPE("MethodType is null",
+                () -> StringConcatFactory.makeConcat(lookup, methodName, null));
+
+        // Advanced factory, check for nulls:
+        failNPE("Lookup is null",
+                () -> StringConcatFactory.makeConcatWithConstants(null, methodName, mt, recipe, constants));
+
+        failNPE("Method name is null",
+                () -> StringConcatFactory.makeConcatWithConstants(lookup, null, mt, recipe, constants));
+
+        failNPE("MethodType is null",
+                () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, null, recipe, constants));
+
+        failNPE("Recipe is null",
+                () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mt, null, constants));
+
+        failNPE("Constants vararg is null",
+                () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mt, recipe, null));
+
+        // Simple factory, check for return type
+        fail("Return type: void",
+                () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(void.class, String.class, int.class)));
+
+        fail("Return type: int",
+                () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(int.class, String.class, int.class)));
+
+        fail("Return type: StringBuilder",
+                () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(StringBuilder.class, String.class, int.class)));
+
+        ok("Return type: Object",
+                () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(Object.class, String.class, int.class)));
+
+        ok("Return type: CharSequence",
+                () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(CharSequence.class, String.class, int.class)));
+
+        ok("Return type: Serializable",
+                () -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(Serializable.class, String.class, int.class)));
+
+        // Advanced factory, check for return types
+        fail("Return type: void",
+                () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(void.class, String.class, int.class), recipe, constants));
+
+        fail("Return type: int",
+                () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(int.class, String.class, int.class), recipe, constants));
+
+        fail("Return type: StringBuilder",
+                () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(StringBuilder.class, String.class, int.class), recipe, constants));
+
+        ok("Return type: Object",
+                () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(Object.class, String.class, int.class), recipe, constants));
+
+        ok("Return type: CharSequence",
+                () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(CharSequence.class, String.class, int.class), recipe, constants));
+
+        ok("Return type: Serializable",
+                () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(Serializable.class, String.class, int.class), recipe, constants));
+
+        // Simple factory: check for dynamic arguments overflow
+        ok("Dynamic arguments is under limit",
+                () -> StringConcatFactory.makeConcat(lookup, methodName, mtUnderThreshold));
+
+        ok("Dynamic arguments is at the limit",
+                () -> StringConcatFactory.makeConcat(lookup, methodName, mtThreshold));
+
+        fail("Dynamic arguments is over the limit",
+                () -> StringConcatFactory.makeConcat(lookup, methodName, mtOverThreshold));
+
+        // Advanced factory: check for dynamic arguments overflow
+        ok("Dynamic arguments is under limit",
+                () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtUnderThreshold, recipeUnderThreshold, constants));
+
+        ok("Dynamic arguments is at the limit",
+                () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeThreshold, constants));
+
+        fail("Dynamic arguments is over the limit",
+                () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtOverThreshold, recipeOverThreshold, constants));
+
+        // Advanced factory: check for mismatched recipe and Constants
+        ok("Static arguments and recipe match",
+                () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeThreshold, "bar"));
+
+        fail("Static arguments and recipe mismatch",
+                () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeThreshold, "bar", "baz"));
+
+        // Advanced factory: check for mismatched recipe and dynamic arguments
+        fail("Dynamic arguments and recipe mismatch",
+                () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeUnderThreshold, constants));
+
+        ok("Dynamic arguments and recipe match",
+                () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeThreshold, constants));
+
+        fail("Dynamic arguments and recipe mismatch",
+                () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeOverThreshold, constants));
+
+        // Test passing array as constant
+        {
+            String[] arg = {"boo", "bar"};
+
+            CallSite cs1 = StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(String.class, int.class), "" + TAG_ARG + TAG_CONST + TAG_CONST, arg);
+            test("42boobar", (String) cs1.getTarget().invokeExact(42));
+        }
+
+        // Test passing null constant
+        ok("Can pass regular constants",
+                () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(String.class, int.class), "" + TAG_ARG + TAG_CONST, "foo"));
+
+        failNPE("Cannot pass null constants",
+                () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(String.class, int.class), "" + TAG_ARG + TAG_CONST, new String[]{null}));
+
+        // Simple factory: test empty arguments
+        ok("Ok to pass empty arguments",
+                () -> StringConcatFactory.makeConcat(lookup, methodName, mtEmpty));
+
+        // Advanced factory: test empty arguments
+        ok("Ok to pass empty arguments",
+                () -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtEmpty, recipeEmpty));
+    }
+
+    public static void ok(String msg, Callable runnable) {
+        try {
+            runnable.call();
+        } catch (Throwable e) {
+            e.printStackTrace();
+            throw new IllegalStateException(msg + ", should have passed", e);
+        }
+    }
+
+    public static void fail(String msg, Callable runnable) {
+        boolean expected = false;
+        try {
+            runnable.call();
+        } catch (StringConcatException e) {
+            expected = true;
+        } catch (Throwable e) {
+            e.printStackTrace();
+        }
+
+        if (!expected) {
+            throw new IllegalStateException(msg + ", should have failed with StringConcatException");
+        }
+    }
+
+
+    public static void failNPE(String msg, Callable runnable) {
+        boolean expected = false;
+        try {
+            runnable.call();
+        } catch (NullPointerException e) {
+            expected = true;
+        } catch (Throwable e) {
+            e.printStackTrace();
+        }
+
+        if (!expected) {
+            throw new IllegalStateException(msg + ", should have failed with NullPointerException");
+        }
+    }
+
+    public static void test(String expected, String actual) {
+       // Fingers crossed: String concat should work.
+       if (!expected.equals(actual)) {
+           StringBuilder sb = new StringBuilder();
+           sb.append("Expected = ");
+           sb.append(expected);
+           sb.append(", actual = ");
+           sb.append(actual);
+           throw new IllegalStateException(sb.toString());
+       }
+    }
+
+}
diff --git a/jdk/test/java/lang/String/concat/update-tests.sh b/jdk/test/java/lang/String/concat/update-tests.sh
new file mode 100644
index 00000000000..533d6e8f0da
--- /dev/null
+++ b/jdk/test/java/lang/String/concat/update-tests.sh
@@ -0,0 +1,26 @@
+# Copyright (c) 2015, 2016, 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.
+
+#!/bin/bash
+
+javac ImplicitStringConcatShapesTestGen.java
+java ImplicitStringConcatShapesTestGen > ImplicitStringConcatShapes.java
+rm ImplicitStringConcatShapesTestGen.class
diff --git a/jdk/test/java/lang/invoke/InvokeDynamicPrintArgs.java b/jdk/test/java/lang/invoke/InvokeDynamicPrintArgs.java
index 7740d5f46ff..6bde4204615 100644
--- a/jdk/test/java/lang/invoke/InvokeDynamicPrintArgs.java
+++ b/jdk/test/java/lang/invoke/InvokeDynamicPrintArgs.java
@@ -28,7 +28,7 @@
  * @compile InvokeDynamicPrintArgs.java
  * @run main/othervm
  *      indify.Indify
- *      --verify-specifier-count=3
+ *      --verify-specifier-count=8
  *      --expand-properties --classpath ${test.classes}
  *      --java test.java.lang.invoke.InvokeDynamicPrintArgs --check-output
  * @run main/othervm
diff --git a/jdk/test/java/lang/invoke/MethodHandleConstants.java b/jdk/test/java/lang/invoke/MethodHandleConstants.java
index 57e041ba7bb..5f64e696ef1 100644
--- a/jdk/test/java/lang/invoke/MethodHandleConstants.java
+++ b/jdk/test/java/lang/invoke/MethodHandleConstants.java
@@ -28,7 +28,7 @@
  * @compile MethodHandleConstants.java
  * @run main/othervm
  *      indify.Indify
- *      --verify-specifier-count=0
+ *      --verify-specifier-count=4
  *      --expand-properties --classpath ${test.classes}
  *      --java test.java.lang.invoke.MethodHandleConstants --check-output
  * @run main/othervm
diff --git a/jdk/test/java/net/SocketPermission/SocketPermissionTest.java b/jdk/test/java/net/SocketPermission/SocketPermissionTest.java
index 5cc25ab0c02..1b68415d028 100644
--- a/jdk/test/java/net/SocketPermission/SocketPermissionTest.java
+++ b/jdk/test/java/net/SocketPermission/SocketPermissionTest.java
@@ -25,8 +25,7 @@
  * @test
  * @bug 8047031
  * @summary SocketPermission tests for legacy socket types
- * @library ../../../lib/testlibrary
- * @run testng/othervm/policy=policy SocketPermissionTest
+ * @run testng/othervm SocketPermissionTest
  * @key intermittent
  */
 import java.io.IOException;
@@ -39,186 +38,279 @@ import java.net.Socket;
 import java.net.SocketPermission;
 import java.security.AccessControlContext;
 import java.security.AccessController;
+import java.security.CodeSource;
 import java.security.Permission;
+import java.security.PermissionCollection;
 import java.security.Permissions;
-import java.security.PrivilegedAction;
+import java.security.Policy;
+import java.security.PrivilegedExceptionAction;
 import java.security.ProtectionDomain;
-import java.util.Arrays;
-import java.util.function.Function;
-import java.util.function.IntConsumer;
-import static jdk.testlibrary.Utils.getFreePort;
+
 import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
 
 public class SocketPermissionTest {
-    private int freePort = -1;
-
-    //positive tests
-    @Test(dataProvider = "positiveProvider")
-    public void testPositive(Function<String, AccessControlContext> genAcc, IntConsumer func) {
-        String addr = "localhost:" + freePort;
-        AccessControlContext acc = genAcc.apply(addr);
-        AccessController.doPrivileged((PrivilegedAction) () -> {
-            func.accept(freePort);
-            return null;
-        }, acc);
-    }
-
-    //negative tests
-    @Test(dataProvider = "negativeProvider", expectedExceptions = SecurityException.class)
-    public void testNegative(AccessControlContext acc, IntConsumer func) {
-        AccessController.doPrivileged((PrivilegedAction) () -> {
-            func.accept(freePort);
-            return null;
-        }, acc);
-    }
 
     @BeforeMethod
-    public void setFreePort() throws Exception {
-        freePort = getFreePort();
+    public void setupSecurityManager() throws Exception {
+        // All permissions, a specific ACC will be used to when testing
+        // with a reduced permission set.
+        Policy.setPolicy(new Policy() {
+             final PermissionCollection perms = new Permissions();
+             { perms.add(new java.security.AllPermission()); }
+             public PermissionCollection getPermissions(ProtectionDomain domain) {
+                 return perms;
+             }
+             public PermissionCollection getPermissions(CodeSource codesource) {
+                 return perms;
+             }
+             public boolean implies(ProtectionDomain domain, Permission perm) {
+                 return perms.implies(perm);
+             }
+        } );
+        System.setSecurityManager(new SecurityManager());
     }
 
-    @DataProvider
-    public Object[][] positiveProvider() {
-        //test for SocketPermission "host:port","connect,resolve";
-        Function<String, AccessControlContext> generateAcc1 = (addr) -> getAccessControlContext(
-                new SocketPermission(addr, "listen, connect,resolve"));
-        IntConsumer func1 = (i) -> connectSocketTest(i);
-        IntConsumer func2 = (i) -> connectDatagramSocketTest(i);
+    static final AccessControlContext RESTRICTED_ACC = getAccessControlContext();
 
-        //test for SocketPermission "localhost:1024-","accept";
-        Function<String, AccessControlContext> generateAcc2 = (addr) -> getAccessControlContext(
-                new SocketPermission(addr, "listen,connect,resolve"),
-                new SocketPermission("localhost:1024-", "accept"));
-        IntConsumer func3 = (i) -> acceptServerSocketTest(i);
+    @Test
+    public void connectSocketTest() throws Exception {
+        try (ServerSocket ss = new ServerSocket(0)) {
+            int port = ss.getLocalPort();
 
-        //test for SocketPermission "229.227.226.221", "connect,accept"
-        Function<String, AccessControlContext> generateAcc3 = (addr) -> getAccessControlContext(
-                new SocketPermission(addr, "listen,resolve"),
-                new SocketPermission("229.227.226.221", "connect,accept"));
-        IntConsumer func4 = (i) -> sendDatagramPacketTest(i);
-        IntConsumer func5 = (i) -> joinGroupMulticastTest(i);
-
-        //test for SocketPermission "host:port", "listen"
-        Function<String, AccessControlContext> generateAcc4 = (addr) -> getAccessControlContext(
-                new SocketPermission(addr, "listen"));
-        IntConsumer func6 = (i) -> listenDatagramSocketTest(i);
-        IntConsumer func7 = (i) -> listenMulticastSocketTest(i);
-        IntConsumer func8 = (i) -> listenServerSocketTest(i);
-
-        return new Object[][]{
-            {generateAcc1, func1},
-            {generateAcc1, func2},
-            {generateAcc2, func3},
-            {generateAcc3, func4},
-            {generateAcc3, func5},
-            {generateAcc4, func6},
-            {generateAcc4, func7},
-            {generateAcc4, func8}
-        };
-    }
-
-    @DataProvider
-    public Object[][] negativeProvider() {
-        IntConsumer[] funcs = {i -> connectSocketTest(i),
-            i -> connectDatagramSocketTest(i), i -> acceptServerSocketTest(i),
-            i -> sendDatagramPacketTest(i), i -> joinGroupMulticastTest(i),
-            i -> listenDatagramSocketTest(i), i -> listenMulticastSocketTest(i),
-            i -> listenServerSocketTest(i)};
-        return Arrays.stream(funcs).map(f -> {
-            //Construct an AccessControlContext without SocketPermission
+            String addr = "localhost:" + port;
             AccessControlContext acc = getAccessControlContext(
-                    new java.io.FilePermission("<<ALL FILES>>", "read,write,execute,delete"),
-                    new java.net.NetPermission("*"),
-                    new java.util.PropertyPermission("*", "read,write"),
-                    new java.lang.reflect.ReflectPermission("*"),
-                    new java.lang.RuntimePermission("*"),
-                    new java.security.SecurityPermission("*"),
-                    new java.io.SerializablePermission("*"));
-            return new Object[]{acc, f};
-        }).toArray(Object[][]::new);
-    }
+                    new SocketPermission(addr, "listen,connect,resolve"));
 
-    public void connectSocketTest(int port) {
-        try (ServerSocket server = new ServerSocket(port);
-                Socket client = new Socket(InetAddress.getLocalHost(), port);) {
-        } catch (IOException ex) {
-            throw new RuntimeException(ex);
+            // Positive
+            AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> {
+                try (Socket client = new Socket(InetAddress.getLocalHost(), port)) {
+                }
+                return null;
+            }, acc);
+
+            //Negative
+            try {
+                AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> {
+                    Socket client = new Socket(InetAddress.getLocalHost(), port);
+                    fail("Expected SecurityException");
+                    return null;
+                }, RESTRICTED_ACC);
+            } catch (SecurityException expected) { }
         }
     }
 
-    public void connectDatagramSocketTest(int port) {
-        String msg = "Hello";
-        try {
-            InetAddress me = InetAddress.getLocalHost();
-            try (DatagramSocket ds = new DatagramSocket(port, me)) {
-                DatagramPacket dp = new DatagramPacket(msg.getBytes(),
-                        msg.length(), me, port);
+    @Test
+    public void connectDatagramSocketTest() throws Exception {
+        byte[] msg = "Hello".getBytes(UTF_8);
+        InetAddress lh = InetAddress.getLocalHost();
+
+        try (DatagramSocket ds = new DatagramSocket(0)) {
+            int port = ds.getLocalPort();
+
+            String addr = lh.getHostAddress() + ":" + port;
+            AccessControlContext acc = getAccessControlContext(
+                    new SocketPermission(addr, "connect,resolve"));
+
+            // Positive
+            AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> {
+                DatagramPacket dp = new DatagramPacket(msg, msg.length, lh, port);
                 ds.send(dp);
-            }
-        } catch (IOException ex) {
-            throw new RuntimeException(ex);
+                return null;
+            }, acc);
+
+            // Negative
+            try {
+                AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> {
+                    DatagramPacket dp = new DatagramPacket(msg, msg.length, lh, port);
+                    ds.send(dp);
+                    fail("Expected SecurityException");
+                    return null;
+                }, RESTRICTED_ACC);
+            } catch (SecurityException expected) { }
         }
     }
 
-    public void acceptServerSocketTest(int port) {
-        try {
-            InetAddress me = InetAddress.getLocalHost();
-            try (ServerSocket server = new ServerSocket(port)) {
-                Socket client = new Socket(me, port);
-                server.accept();
-            }
-        } catch (IOException ex) {
-            throw new RuntimeException(ex);
+    @Test
+    public void acceptServerSocketTest() throws Exception {
+        try (ServerSocket ss = new ServerSocket(0)) {
+            int port = ss.getLocalPort();
+
+            String addr = "localhost:" + port;
+            AccessControlContext acc = getAccessControlContext(
+                    new SocketPermission(addr, "listen,connect,resolve"),
+                    new SocketPermission("localhost:1024-", "accept"));
+
+            // Positive
+            AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> {
+                InetAddress me = InetAddress.getLocalHost();
+                try (Socket client = new Socket(me, port)) {
+                    ss.accept();
+                }
+                return null;
+            }, acc);
+
+            // Negative
+            try {
+                AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> {
+                    InetAddress me = InetAddress.getLocalHost();
+                    try (Socket client = new Socket(me, port)) {
+                        ss.accept();
+                    }
+                    fail("Expected SecurityException");
+                    return null;
+                }, RESTRICTED_ACC);
+            } catch (SecurityException expected) { }
         }
     }
 
-    public static void sendDatagramPacketTest(int port) {
-        String msg = "Hello";
-        try {
-            InetAddress group = InetAddress.getByName("229.227.226.221");
-            try (DatagramSocket s = new DatagramSocket(port)) {
-                DatagramPacket hi = new DatagramPacket(msg.getBytes(),
-                        msg.length(), group, port);
-                s.send(hi);
-            }
-        } catch (IOException ex) {
-            throw new RuntimeException(ex);
+    @Test
+    public void sendDatagramPacketTest() throws Exception {
+        byte[] msg = "Hello".getBytes(UTF_8);
+        InetAddress group = InetAddress.getByName("229.227.226.221");
+
+        try (DatagramSocket ds = new DatagramSocket(0)) {
+            int port = ds.getLocalPort();
+
+            String addr = "localhost:" + port;
+            //test for SocketPermission "229.227.226.221", "connect,accept"
+            AccessControlContext acc = getAccessControlContext(
+                    new SocketPermission(addr, "listen,resolve"),
+                    new SocketPermission("229.227.226.221", "connect,accept"));
+
+            // Positive
+            AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> {
+                DatagramPacket hi = new DatagramPacket(msg, msg.length, group, port);
+                ds.send(hi);
+                return null;
+            }, acc);
+
+            // Negative
+            try {
+                AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> {
+                    DatagramPacket hi = new DatagramPacket(msg, msg.length, group, port);
+                    ds.send(hi);
+                    fail("Expected SecurityException");
+                    return null;
+                }, RESTRICTED_ACC);
+            } catch (SecurityException expected) { }
         }
     }
 
-    public void joinGroupMulticastTest(int port) {
-        try {
-            InetAddress group = InetAddress.getByName("229.227.226.221");
-            try (MulticastSocket s = new MulticastSocket(port)) {
+    @Test
+    public void joinGroupMulticastTest() throws Exception {
+        InetAddress group = InetAddress.getByName("229.227.226.221");
+        try (MulticastSocket s = new MulticastSocket(0)) {
+            int port = s.getLocalPort();
+
+            String addr = "localhost:" + port;
+            AccessControlContext acc = getAccessControlContext(
+                    new SocketPermission(addr, "listen,resolve"),
+                    new SocketPermission("229.227.226.221", "connect,accept"));
+
+            // Positive
+            AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> {
                 s.joinGroup(group);
                 s.leaveGroup(group);
-            }
-        } catch (IOException ex) {
-            throw new RuntimeException(ex);
+                return null;
+            }, acc);
+
+            // Negative
+            try {
+                AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> {
+                    s.joinGroup(group);
+                    s.leaveGroup(group);
+                    fail("Expected SecurityException");
+                    return null;
+                }, RESTRICTED_ACC);
+            } catch (SecurityException expected) { }
         }
+
     }
 
-    public void listenDatagramSocketTest(int port) {
-        try (DatagramSocket ds = new DatagramSocket(port)) {
-        } catch (IOException ex) {
-            throw new RuntimeException(ex);
-        }
+    @Test
+    public void listenDatagramSocketTest() throws Exception {
+        // the hardcoded port number doesn't really matter since we expect the
+        // security permission to be checked before the underlying operation.
+        int port = 8899;
+        String addr = "localhost:" + port;
+        AccessControlContext acc = getAccessControlContext(
+                new SocketPermission(addr, "listen"));
+
+        // Positive
+        AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> {
+            try (DatagramSocket ds = new DatagramSocket(port)) { }
+            catch (IOException intermittentlyExpected) { /* ignore */ }
+            return null;
+        }, acc);
+
+        // Negative
+        try {
+            AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> {
+                try (DatagramSocket ds = new DatagramSocket(port)) { }
+                catch (IOException intermittentlyExpected) { /* ignore */ }
+                fail("Expected SecurityException");
+                return null;
+            }, RESTRICTED_ACC);
+        } catch (SecurityException expected) { }
     }
 
-    public void listenMulticastSocketTest(int port) {
-        try (MulticastSocket ms = new MulticastSocket(port)) {
-        } catch (IOException ex) {
-            throw new RuntimeException(ex);
-        }
+    @Test
+    public void listenMulticastSocketTest() throws Exception {
+        // the hardcoded port number doesn't really matter since we expect the
+        // security permission to be checked before the underlying operation.
+        int port = 8899;
+        String addr = "localhost:" + port;
+        AccessControlContext acc = getAccessControlContext(
+                new SocketPermission(addr, "listen"));
+
+        // Positive
+        AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> {
+            try (MulticastSocket ms = new MulticastSocket(port)) { }
+            catch (IOException intermittentlyExpected) { /* ignore */ }
+            return null;
+        }, acc);
+
+        // Negative
+        try {
+            AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> {
+                try (MulticastSocket ms = new MulticastSocket(port)) { }
+                catch (IOException intermittentlyExpected) { /* ignore */ }
+                fail("Expected SecurityException");
+                return null;
+            }, RESTRICTED_ACC);
+        } catch (SecurityException expected) { }
     }
 
-    public void listenServerSocketTest(int port) {
-        try (ServerSocket ms = new ServerSocket(port)) {
-        } catch (IOException ex) {
-            throw new RuntimeException(ex);
-        }
+    @Test
+    public void listenServerSocketTest() throws Exception {
+        // the hardcoded port number doesn't really matter since we expect the
+        // security permission to be checked before the underlying operation.
+        int port = 8899;
+        String addr = "localhost:" + port;
+        AccessControlContext acc = getAccessControlContext(
+                new SocketPermission(addr, "listen"));
+
+        // Positive
+        AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> {
+            try (ServerSocket ss = new ServerSocket(port)) { }
+            catch (IOException intermittentlyExpected) { /* ignore */ }
+            return null;
+        }, acc);
+
+        // Negative
+        try {
+            AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> {
+                try (ServerSocket ss = new ServerSocket(port)) { }
+                catch (IOException intermittentlyExpected) { /* ignore */ }
+                fail("Expected SecurityException");
+                return null;
+            }, RESTRICTED_ACC);
+        } catch (SecurityException expected) { }
+
     }
 
     private static AccessControlContext getAccessControlContext(Permission... ps) {
@@ -234,4 +326,15 @@ public class SocketPermissionTest {
         return new AccessControlContext(new ProtectionDomain[]{pd});
     }
 
+    // Standalone entry point for running with, possibly older, JDKs.
+    public static void main(String[] args) throws Throwable {
+        SocketPermissionTest test = new SocketPermissionTest();
+        test.setupSecurityManager();
+        for (java.lang.reflect.Method m : SocketPermissionTest.class.getDeclaredMethods()) {
+            if (m.getAnnotation(Test.class) != null) {
+                System.out.println("Invoking " + m.getName());
+                m.invoke(test);
+            }
+        }
+    }
 }
diff --git a/jdk/test/java/net/SocketPermission/policy b/jdk/test/java/net/SocketPermission/policy
deleted file mode 100644
index 2a71c848da8..00000000000
--- a/jdk/test/java/net/SocketPermission/policy
+++ /dev/null
@@ -1,3 +0,0 @@
-grant {
- permission java.security.AllPermission;
-};
diff --git a/jdk/test/java/net/URI/URItoURLTest.java b/jdk/test/java/net/URI/URItoURLTest.java
index e1a5577bb74..05fadebf8f9 100644
--- a/jdk/test/java/net/URI/URItoURLTest.java
+++ b/jdk/test/java/net/URI/URItoURLTest.java
@@ -23,13 +23,16 @@
 
 /**
  * @test
- * @bug  4768755 4677045
- * @summary URL.equal(URL) is inconsistant for opaque URI.toURL()
- *                      and new URL(URI.toString)
+ * @bug  4768755 4677045 8147462
+ * @summary URL.equal(URL) is inconsistent for opaque URI.toURL()
+ *              and new URL(URI.toString)
  *          URI.toURL() does not always work as specified
+ *          Ensure URIs representing invalid/malformed URLs throw similar
+ *              exception with new URL(URI.toString()) and URI.toURL()
  */
 
 import java.net.*;
+import java.util.Objects;
 
 public class URItoURLTest {
 
@@ -39,19 +42,43 @@ public class URItoURLTest {
         URL classUrl = testClass.getClass().
                                     getResource("/java/lang/Object.class");
 
-        String[] uris = { "mailto:xyz@abc.de",
+        String[] uris = {
+                        "mailto:xyz@abc.de",
                         "file:xyz#ab",
                         "http:abc/xyz/pqr",
+                        "http:abc/xyz/pqr?id=x%0a&ca=true",
                         "file:/C:/v700/dev/unitTesting/tests/apiUtil/uri",
                         "http:///p",
+                        "file:/C:/v700/dev/unitTesting/tests/apiUtil/uri",
+                        "file:/C:/v700/dev%20src/unitTesting/tests/apiUtil/uri",
+                        "file:/C:/v700/dev%20src/./unitTesting/./tests/apiUtil/uri",
+                        "http://localhost:80/abc/./xyz/../pqr?id=x%0a&ca=true",
+                        "file:./test/./x",
+                        "file:./././%20#i=3",
+                        "file:?hmm",
+                        "file:.#hmm",
                         classUrl.toExternalForm(),
                         };
 
+        // Strings that represent valid URIs but invalid URLs that should throw
+        // MalformedURLException both when calling toURL and new URL(String)
+        String[] malformedUrls = {
+                        "test:/test",
+                        "fiel:test",
+                        };
+
+        // Non-absolute URIs should throw IAE when calling toURL but will throw
+        // MalformedURLException when calling new URL
+        String[] illegalUris = {
+                        "./test",
+                        "/test",
+                        };
+
         boolean isTestFailed = false;
         boolean isURLFailed = false;
 
-        for (int i = 0; i < uris.length; i++) {
-            URI uri = URI.create(uris[i]);
+        for (String uriString : uris) {
+            URI uri = URI.create(uriString);
 
             URL url1 = new URL(uri.toString());
             URL url2 = uri.toURL();
@@ -107,6 +134,42 @@ public class URItoURLTest {
             System.out.println();
             isURLFailed = false;
         }
+        for (String malformedUrl : malformedUrls) {
+            Exception toURLEx = null;
+            Exception newURLEx = null;
+            try {
+                new URI(malformedUrl).toURL();
+            } catch (Exception e) {
+                // expected
+                toURLEx = e;
+            }
+            try {
+                new URL(new URI(malformedUrl).toString());
+            } catch (Exception e) {
+                // expected
+                newURLEx = e;
+            }
+            if (!(toURLEx instanceof MalformedURLException) ||
+                    !(newURLEx instanceof MalformedURLException) ||
+                    !toURLEx.getMessage().equals(newURLEx.getMessage())) {
+                isTestFailed = true;
+                System.out.println("Expected the same MalformedURLException: " +
+                    newURLEx + " vs " + toURLEx);
+            }
+        }
+        for (String illegalUri : illegalUris) {
+             try {
+                 new URI(illegalUri).toURL();
+             } catch (IllegalArgumentException e) {
+                 // pass
+             }
+
+             try {
+                 new URL(illegalUri);
+             } catch (MalformedURLException e) {
+                 // pass
+             }
+        }
         if (isTestFailed) {
             throw new Exception("URI.toURL() test failed");
         }
diff --git a/jdk/test/java/net/URL/LocaleDependentURLTest.java b/jdk/test/java/net/URL/LocaleDependentURLTest.java
new file mode 100644
index 00000000000..caa967698b2
--- /dev/null
+++ b/jdk/test/java/net/URL/LocaleDependentURLTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2016, 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 8147962
+ * @summary Verify URLs with upper-case protocols parse correctly in any
+ *          locale
+ * @run main/othervm LocaleDependentURLTest
+ */
+import java.net.*;
+import java.util.Locale;
+
+public class LocaleDependentURLTest {
+
+    public static void main(String args[]) throws MalformedURLException {
+        for (Locale locale : Locale.getAvailableLocales()) {
+            Locale.setDefault(locale);
+            new URL("FILE:///TMP/X");
+        }
+    }
+}
diff --git a/jdk/test/java/nio/file/WatchService/LotsOfCloses.java b/jdk/test/java/nio/file/WatchService/LotsOfCloses.java
new file mode 100644
index 00000000000..44ec26180b3
--- /dev/null
+++ b/jdk/test/java/nio/file/WatchService/LotsOfCloses.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2016, 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 8148192
+ * @summary Invoking close asynchronously can cause register to fail with
+ *     an IOException rather than a ClosedWatchServiceException
+ * @run main LotsOfCloses
+ */
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.ClosedWatchServiceException;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardWatchEventKinds;
+import java.nio.file.WatchService;
+import java.util.Random;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+public class LotsOfCloses {
+
+    private static final Random RAND = new Random();
+
+    public static void main(String[] args) throws Exception {
+        Path dir = Files.createTempDirectory("tmp");
+        ExecutorService pool = Executors.newCachedThreadPool();
+        try {
+
+            // run the test repeatedly
+            long start = System.currentTimeMillis();
+            while ((System.currentTimeMillis() - start) < 5000) {
+                test(dir, pool);
+            }
+
+        } finally {
+            pool.shutdown();
+        }
+
+    }
+
+    /**
+     * Create a WatchService to watch for changes in the given directory
+     * and then attempt to close the WatchService and change a registration
+     * at the same time.
+     */
+    static void test(Path dir, ExecutorService pool) throws Exception {
+        WatchService watcher = FileSystems.getDefault().newWatchService();
+
+        // initial registration
+        dir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE);
+
+        // submit tasks to close the WatchService and update the registration
+        Future<Void> closeResult;
+        Future<Boolean> registerResult;
+
+        if (RAND.nextBoolean()) {
+            closeResult = pool.submit(newCloserTask(watcher));
+            registerResult = pool.submit(newRegisterTask(watcher, dir));
+        } else {
+            registerResult = pool.submit(newRegisterTask(watcher, dir));
+            closeResult = pool.submit(newCloserTask(watcher));
+        }
+
+        closeResult.get();
+        registerResult.get();
+
+    }
+
+    /**
+     * Returns a task that closes the given WatchService.
+     */
+    static Callable<Void> newCloserTask(WatchService watcher) {
+        return () ->  {
+            try {
+                watcher.close();
+                return null;
+            } catch (IOException ioe) {
+                throw new UncheckedIOException(ioe);
+            }
+        };
+    }
+
+    /**
+     * Returns a task that updates the registration of a directory with
+     * a WatchService.
+     */
+    static Callable<Boolean> newRegisterTask(WatchService watcher, Path dir) {
+        return () -> {
+            try {
+                dir.register(watcher, StandardWatchEventKinds.ENTRY_DELETE);
+                return true;
+            } catch (ClosedWatchServiceException e) {
+                return false;
+            } catch (IOException ioe) {
+                throw new UncheckedIOException(ioe);
+            }
+        };
+    }
+}
diff --git a/jdk/test/java/util/ArrayList/ArrayManagement.java b/jdk/test/java/util/ArrayList/ArrayManagement.java
index c3d690871c4..81adc716013 100644
--- a/jdk/test/java/util/ArrayList/ArrayManagement.java
+++ b/jdk/test/java/util/ArrayList/ArrayManagement.java
@@ -83,6 +83,7 @@ public class ArrayManagement {
         int oldCapacity = capacity(list);
         int oldModCount = modCount(list);
         list.ensureCapacity(capacity);
+        assertTrue(capacity(list) >= capacity || capacity(list) == 0);
         assertEquals(modCount(list),
                      (capacity(list) == oldCapacity)
                      ? oldModCount
diff --git a/jdk/test/java/util/Map/Defaults.java b/jdk/test/java/util/Map/Defaults.java
index 37c2fad21dc..7b2956f8846 100644
--- a/jdk/test/java/util/Map/Defaults.java
+++ b/jdk/test/java/util/Map/Defaults.java
@@ -48,11 +48,14 @@ import java.util.WeakHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentSkipListMap;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.BiFunction;
+import java.util.function.Function;
 import java.util.function.Supplier;
 
 import org.testng.annotations.Test;
 import org.testng.annotations.DataProvider;
+import static java.util.Objects.requireNonNull;
 import static org.testng.Assert.fail;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
@@ -533,6 +536,191 @@ public class Defaults {
                 "Should throw NPE");
     }
 
+    /** A function that flipflops between running two other functions. */
+    static <T,U,V> BiFunction<T,U,V> twoStep(AtomicBoolean b,
+                                             BiFunction<T,U,V> first,
+                                             BiFunction<T,U,V> second) {
+        return (t, u) -> {
+            boolean bb = b.get();
+            try {
+                return (b.get() ? first : second).apply(t, u);
+            } finally {
+                b.set(!bb);
+            }};
+    }
+
+    /**
+     * Simulates races by modifying the map within the mapping function.
+     */
+    @Test
+    public void testConcurrentMap_computeIfAbsent_racy() {
+        final ConcurrentMap<Long,Long> map = new ImplementsConcurrentMap<>();
+        final Long two = 2L;
+        Function<Long,Long> f, g;
+
+        // race not detected if function returns null
+        f = (k) -> { map.put(two, 42L); return null; };
+        assertNull(map.computeIfAbsent(two, f));
+        assertEquals(42L, (long)map.get(two));
+
+        map.clear();
+        f = (k) -> { map.put(two, 42L); return 86L; };
+        assertEquals(42L, (long)map.computeIfAbsent(two, f));
+        assertEquals(42L, (long)map.get(two));
+
+        // mapping function ignored if value already exists
+        map.put(two, 99L);
+        assertEquals(99L, (long)map.computeIfAbsent(two, f));
+        assertEquals(99L, (long)map.get(two));
+    }
+
+    /**
+     * Simulates races by modifying the map within the remapping function.
+     */
+    @Test
+    public void testConcurrentMap_computeIfPresent_racy() {
+        final AtomicBoolean b = new AtomicBoolean(true);
+        final ConcurrentMap<Long,Long> map = new ImplementsConcurrentMap<>();
+        final Long two = 2L;
+        BiFunction<Long,Long,Long> f, g;
+
+        for (Long val : new Long[] { null, 86L }) {
+            map.clear();
+
+            // Function not invoked if no mapping exists
+            f = (k, v) -> { map.put(two, 42L); return val; };
+            assertNull(map.computeIfPresent(two, f));
+            assertNull(map.get(two));
+
+            map.put(two, 42L);
+            f = (k, v) -> { map.put(two, 86L); return val; };
+            g = (k, v) -> {
+                assertSame(two, k);
+                assertEquals(86L, (long)v);
+                return null;
+            };
+            assertNull(map.computeIfPresent(two, twoStep(b, f, g)));
+            assertFalse(map.containsKey(two));
+            assertTrue(b.get());
+
+            map.put(two, 42L);
+            f = (k, v) -> { map.put(two, 86L); return val; };
+            g = (k, v) -> {
+                assertSame(two, k);
+                assertEquals(86L, (long)v);
+                return 99L;
+            };
+            assertEquals(99L, (long)map.computeIfPresent(two, twoStep(b, f, g)));
+            assertTrue(map.containsKey(two));
+            assertTrue(b.get());
+        }
+    }
+
+    @Test
+    public void testConcurrentMap_compute_simple() {
+        final ConcurrentMap<Long,Long> map = new ImplementsConcurrentMap<>();
+        BiFunction<Long,Long,Long> fun = (k, v) -> ((v == null) ? 0L : k + v);
+        assertEquals(Long.valueOf(0L), map.compute(3L, fun));
+        assertEquals(Long.valueOf(3L), map.compute(3L, fun));
+        assertEquals(Long.valueOf(6L), map.compute(3L, fun));
+        assertNull(map.compute(3L, (k, v) -> null));
+        assertTrue(map.isEmpty());
+
+        assertEquals(Long.valueOf(0L), map.compute(new Long(3L), fun));
+        assertEquals(Long.valueOf(3L), map.compute(new Long(3L), fun));
+        assertEquals(Long.valueOf(6L), map.compute(new Long(3L), fun));
+        assertNull(map.compute(3L, (k, v) -> null));
+        assertTrue(map.isEmpty());
+    }
+
+    /**
+     * Simulates races by modifying the map within the remapping function.
+     */
+    @Test
+    public void testConcurrentMap_compute_racy() {
+        final AtomicBoolean b = new AtomicBoolean(true);
+        final ConcurrentMap<Long,Long> map = new ImplementsConcurrentMap<>();
+        final Long two = 2L;
+        BiFunction<Long,Long,Long> f, g;
+
+        // null -> null is a no-op; race not detected
+        f = (k, v) -> { map.put(two, 42L); return null; };
+        assertNull(map.compute(two, f));
+        assertEquals(42L, (long)map.get(two));
+
+        for (Long val : new Long[] { null, 86L }) {
+            map.clear();
+
+            f = (k, v) -> { map.put(two, 42L); return 86L; };
+            g = (k, v) -> {
+                assertSame(two, k);
+                assertEquals(42L, (long)v);
+                return k + v;
+            };
+            assertEquals(44L, (long)map.compute(two, twoStep(b, f, g)));
+            assertEquals(44L, (long)map.get(two));
+            assertTrue(b.get());
+
+            f = (k, v) -> { map.remove(two); return val; };
+            g = (k, v) -> {
+                assertSame(two, k);
+                assertNull(v);
+                return 44L;
+            };
+            assertEquals(44L, (long)map.compute(two, twoStep(b, f, g)));
+            assertEquals(44L, (long)map.get(two));
+            assertTrue(map.containsKey(two));
+            assertTrue(b.get());
+
+            f = (k, v) -> { map.remove(two); return val; };
+            g = (k, v) -> {
+                assertSame(two, k);
+                assertNull(v);
+                return null;
+            };
+            assertNull(map.compute(two, twoStep(b, f, g)));
+            assertNull(map.get(two));
+            assertFalse(map.containsKey(two));
+            assertTrue(b.get());
+        }
+    }
+
+    /**
+     * Simulates races by modifying the map within the remapping function.
+     */
+    @Test
+    public void testConcurrentMap_merge_racy() {
+        final AtomicBoolean b = new AtomicBoolean(true);
+        final ConcurrentMap<Long,Long> map = new ImplementsConcurrentMap<>();
+        final Long two = 2L;
+        BiFunction<Long,Long,Long> f, g;
+
+        for (Long val : new Long[] { null, 86L }) {
+            map.clear();
+
+            f = (v, w) -> { throw new AssertionError(); };
+            assertEquals(99L, (long)map.merge(two, 99L, f));
+            assertEquals(99L, (long)map.get(two));
+
+            f = (v, w) -> { map.put(two, 42L); return val; };
+            g = (v, w) -> {
+                assertEquals(42L, (long)v);
+                assertEquals(3L, (long)w);
+                return v + w;
+            };
+            assertEquals(45L, (long)map.merge(two, 3L, twoStep(b, f, g)));
+            assertEquals(45L, (long)map.get(two));
+            assertTrue(b.get());
+
+            f = (v, w) -> { map.remove(two); return val; };
+            g = (k, v) -> { throw new AssertionError(); };
+            assertEquals(55L, (long)map.merge(two, 55L, twoStep(b, f, g)));
+            assertEquals(55L, (long)map.get(two));
+            assertTrue(map.containsKey(two));
+            assertFalse(b.get()); b.set(true);
+        }
+    }
+
     public enum IntegerEnum {
 
         e0, e1, e2, e3, e4, e5, e6, e7, e8, e9,
@@ -838,13 +1026,13 @@ public class Defaults {
 
         protected ExtendsAbstractMap(M map) { this.map = map; }
 
-        public Set<Map.Entry<K,V>> entrySet() {
+        @Override public Set<Map.Entry<K,V>> entrySet() {
             return new AbstractSet<Map.Entry<K,V>>() {
-                public int size() {
+                @Override public int size() {
                     return map.size();
                 }
 
-                public Iterator<Map.Entry<K,V>> iterator() {
+                @Override public Iterator<Map.Entry<K,V>> iterator() {
                     final Iterator<Map.Entry<K,V>> source = map.entrySet().iterator();
                     return new Iterator<Map.Entry<K,V>>() {
                        public boolean hasNext() { return source.hasNext(); }
@@ -853,20 +1041,20 @@ public class Defaults {
                     };
                 }
 
-                public boolean add(Map.Entry<K,V> e) {
+                @Override public boolean add(Map.Entry<K,V> e) {
                     return map.entrySet().add(e);
                 }
             };
         }
 
-        public V put(K key, V value) {
+        @Override public V put(K key, V value) {
             return map.put(key, value);
         }
     }
 
     /**
      * A simple mutable concurrent map implementation that provides only default
-     * implementations of all methods. ie. none of the ConcurrentMap interface
+     * implementations of all methods, i.e. none of the ConcurrentMap interface
      * default methods have overridden implementations.
      *
      * @param <K> Type of keys
@@ -875,14 +1063,26 @@ public class Defaults {
     public static class ImplementsConcurrentMap<K,V> extends ExtendsAbstractMap<ConcurrentMap<K,V>, K, V> implements ConcurrentMap<K,V> {
         public ImplementsConcurrentMap() { super(new ConcurrentHashMap<K,V>()); }
 
-        // ConcurrentMap reabstracts these methods
+        // ConcurrentMap reabstracts these methods.
+        //
+        // Unlike ConcurrentHashMap, we have zero tolerance for null values.
 
-        public V replace(K k, V v) { return map.replace(k, v); };
+        @Override public V replace(K k, V v) {
+            return map.replace(requireNonNull(k), requireNonNull(v));
+        }
 
-        public boolean replace(K k, V v, V vv) { return map.replace(k, v, vv); };
+        @Override public boolean replace(K k, V v, V vv) {
+            return map.replace(requireNonNull(k),
+                               requireNonNull(v),
+                               requireNonNull(vv));
+        }
 
-        public boolean remove(Object k, Object v) { return map.remove(k, v); }
+        @Override public boolean remove(Object k, Object v) {
+            return map.remove(requireNonNull(k), requireNonNull(v));
+        }
 
-        public V putIfAbsent(K k, V v) { return map.putIfAbsent(k, v); }
+        @Override public V putIfAbsent(K k, V v) {
+            return map.putIfAbsent(requireNonNull(k), requireNonNull(v));
+        }
     }
 }
diff --git a/jdk/test/java/util/Vector/ArrayManagement.java b/jdk/test/java/util/Vector/ArrayManagement.java
new file mode 100644
index 00000000000..ce4e695487c
--- /dev/null
+++ b/jdk/test/java/util/Vector/ArrayManagement.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2016 Google, Inc.  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 8148174
+ * @summary brittle white box test of internal array management
+ * @run testng ArrayManagement
+ */
+
+import java.lang.reflect.Field;
+import java.util.AbstractList;
+import java.util.Vector;
+import java.util.Collections;
+import java.util.List;
+import java.util.SplittableRandom;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+public class ArrayManagement {
+
+    /**
+     * A Vector that exposes all protected elements, and checks class
+     * invariants.
+     */
+    static class PublicVector<E> extends Vector<E> {
+        public PublicVector() { super(); }
+        public PublicVector(int capacity) { super(capacity); }
+        public PublicVector(int capacity, int capacityIncrement) {
+            super(capacity, capacityIncrement);
+        }
+        public Object[] elementData()  { return elementData; }
+        public int modCount()          { return modCount; }
+        public int capacityIncrement() { return capacityIncrement; }
+        public int capacity()          { return elementData.length; }
+
+        public void ensureCapacity(int minCapacity) {
+            int oldCapacity = capacity();
+            int oldModCount = modCount();
+            super.ensureCapacity(minCapacity);
+            assertTrue(capacity() >= minCapacity);
+            if (minCapacity <= oldCapacity)
+                assertEquals(capacity(), oldCapacity);
+            if (minCapacity > 0)
+                assertEquals(modCount(), oldModCount + 1);
+        }
+    }
+
+    static final int DEFAULT_CAPACITY = 10;
+    static final SplittableRandom rnd = new SplittableRandom();
+
+    static int newCapacity(int oldCapacity) {
+        return 2 * oldCapacity;
+    }
+
+    static List<Object> singletonList() {
+        return Collections.singletonList(Boolean.TRUE);
+    }
+
+    /** Opportunistically randomly test various add operations. */
+    static void addOneElement(PublicVector<Object> list) {
+        int size = list.size();
+        int modCount = list.modCount();
+        switch (rnd.nextInt(4)) {
+        case 0: assertTrue(list.add(Boolean.TRUE)); break;
+        case 1: list.add(size, Boolean.TRUE); break;
+        case 2: assertTrue(list.addAll(singletonList())); break;
+        case 3: assertTrue(list.addAll(size, singletonList())); break;
+        default: throw new AssertionError();
+        }
+        assertEquals(list.modCount(), modCount + 1);
+        assertEquals(list.size(), size + 1);
+    }
+
+    @Test public void defaultCapacity() {
+        PublicVector<Object> list = new PublicVector<>();
+        assertEquals(new PublicVector<Object>().capacity(), DEFAULT_CAPACITY);
+        for (int i = 0; i < DEFAULT_CAPACITY; i++) {
+            addOneElement(list);
+            assertEquals(list.capacity(), DEFAULT_CAPACITY);
+        }
+        addOneElement(list);
+        assertEquals(list.capacity(), newCapacity(DEFAULT_CAPACITY));
+    }
+
+    @Test public void defaultCapacityEnsureCapacity() {
+        PublicVector<Object> list = new PublicVector<>();
+        for (int i = 0; i <= DEFAULT_CAPACITY; i++) {
+            list.ensureCapacity(i);     // no-op!
+            assertEquals(list.capacity(), DEFAULT_CAPACITY);
+        }
+        for (int i = 0; i < DEFAULT_CAPACITY; i++) {
+            addOneElement(list);
+            assertEquals(list.capacity(), DEFAULT_CAPACITY);
+        }
+        addOneElement(list);
+        assertEquals(list.capacity(), newCapacity(DEFAULT_CAPACITY));
+        {
+            int capacity = list.capacity();
+            list.ensureCapacity(capacity + 1);
+            assertEquals(list.capacity(), newCapacity(capacity));
+        }
+        {
+            int capacity = list.capacity();
+            list.ensureCapacity(3 * capacity);
+            assertEquals(list.capacity(), 3 * capacity);
+        }
+    }
+
+    @Test public void ensureCapacityBeyondDefaultCapacity() {
+        PublicVector<Object> list = new PublicVector<>();
+        list.ensureCapacity(DEFAULT_CAPACITY + 1);
+        assertEquals(list.capacity(), newCapacity(DEFAULT_CAPACITY));
+    }
+
+    @Test public void explicitZeroCapacity() {
+        PublicVector<Object> list = new PublicVector<>(0);
+        assertEquals(list.capacity(), 0);
+        addOneElement(list);
+        assertEquals(list.capacity(), 1);
+        addOneElement(list);
+        assertEquals(list.capacity(), 2);
+        addOneElement(list);
+        assertEquals(list.capacity(), 4);
+        addOneElement(list);
+        assertEquals(list.capacity(), 4);
+        addOneElement(list);
+        assertEquals(list.capacity(), 8);
+        addOneElement(list);
+        assertEquals(list.capacity(), 8);
+        addOneElement(list);
+        assertEquals(list.capacity(), 8);
+        list.clear();
+        assertEquals(list.capacity(), 8);
+    }
+
+    @Test public void explicitZeroCapacityWithCapacityIncrement() {
+        PublicVector<Object> list = new PublicVector<>(0, 2);
+        assertEquals(list.capacity(), 0);
+        addOneElement(list);
+        assertEquals(list.capacity(), 2);
+        addOneElement(list);
+        assertEquals(list.capacity(), 2);
+        addOneElement(list);
+        assertEquals(list.capacity(), 4);
+        addOneElement(list);
+        assertEquals(list.capacity(), 4);
+        addOneElement(list);
+        assertEquals(list.capacity(), 6);
+        addOneElement(list);
+        assertEquals(list.capacity(), 6);
+        addOneElement(list);
+        assertEquals(list.capacity(), 8);
+        list.clear();
+        assertEquals(list.capacity(), 8);
+    }
+
+    @Test public void explicitLargeCapacity() {
+        int n = DEFAULT_CAPACITY * 3;
+        PublicVector<Object> list = new PublicVector<>(n);
+        assertEquals(list.capacity(), n);
+        list.ensureCapacity(0);
+        list.ensureCapacity(n);
+        for (int i = 0; i < n; i++) addOneElement(list);
+        assertEquals(list.capacity(), n);
+
+        addOneElement(list);
+        assertEquals(list.capacity(), newCapacity(n));
+    }
+
+    @Test public void explicitLargeCapacityWithCapacityIncrement() {
+        int n = DEFAULT_CAPACITY * 3;
+        PublicVector<Object> list = new PublicVector<>(n, 2);
+        assertEquals(list.capacity(), n);
+        list.ensureCapacity(0);
+        list.ensureCapacity(n);
+        for (int i = 0; i < n; i++) addOneElement(list);
+        assertEquals(list.capacity(), n);
+
+        addOneElement(list);
+        assertEquals(list.capacity(), n + 2);
+    }
+
+    @Test public void emptyArraysAreNotShared() {
+        assertNotSame(new PublicVector<Object>(0).elementData(),
+                      new PublicVector<Object>(0).elementData());
+    }
+
+    @Test public void negativeCapacity() {
+        for (int capacity : new int[] { -1, Integer.MIN_VALUE }) {
+            try {
+                new Vector<Object>(capacity);
+                fail("should throw");
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+}
diff --git a/jdk/test/java/util/Vector/Bug8148174.java b/jdk/test/java/util/Vector/Bug8148174.java
new file mode 100644
index 00000000000..a987cd39155
--- /dev/null
+++ b/jdk/test/java/util/Vector/Bug8148174.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2016 Google, Inc.  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 8148174
+ * @summary repro for: NegativeArraySizeException in Vector.grow(int)
+ * @run main/othervm -Xmx17g Bug8148174
+ * @ignore This test has huge memory requirements
+ */
+
+public class Bug8148174 {
+    public static void main(String[] args) {
+        int size = Integer.MAX_VALUE - 2;
+        java.util.Vector<Object> huge = new java.util.Vector<>(size);
+        for (int i = 0; i < size; i++)
+            huge.add(null);
+        try {
+            huge.addAll(huge);
+            throw new Error("expected OutOfMemoryError not thrown");
+        } catch (OutOfMemoryError success) {}
+    }
+}
diff --git a/jdk/test/java/util/concurrent/forkjoin/FJExceptionTableLeak.java b/jdk/test/java/util/concurrent/forkjoin/FJExceptionTableLeak.java
index c236bd231cc..8b0a92d2613 100644
--- a/jdk/test/java/util/concurrent/forkjoin/FJExceptionTableLeak.java
+++ b/jdk/test/java/util/concurrent/forkjoin/FJExceptionTableLeak.java
@@ -36,17 +36,17 @@
  * @author Doug Lea
  * @bug 8004138
  * @summary Check if ForkJoinPool table leaks thrown exceptions.
- * @run main/othervm -Xmx2200k FJExceptionTableLeak
- * @key intermittent
+ * @run main/othervm -Xmx8m -Djava.util.concurrent.ForkJoinPool.common.parallelism=4 FJExceptionTableLeak
  */
 
 import java.util.concurrent.ForkJoinPool;
 import java.util.concurrent.RecursiveAction;
 
 public class FJExceptionTableLeak {
-    // This test was observed to fail with jdk7 -Xmx2200k,
+    // This test was observed to fail with pre-bug-fix jdk7 -Xmx8m,
     // using STEPS = 220 and TASKS_PER_STEP = 100
-    static final int STEPS = 500;
+    static final int PRE_BUG_FIX_FAILURE_STEPS = 220;
+    static final int STEPS = 10 * PRE_BUG_FIX_FAILURE_STEPS;
     static final int TASKS_PER_STEP = 100;
 
     static class FailingTaskException extends RuntimeException {}
diff --git a/jdk/test/java/util/concurrent/tck/AbstractExecutorServiceTest.java b/jdk/test/java/util/concurrent/tck/AbstractExecutorServiceTest.java
new file mode 100644
index 00000000000..9e140da9f72
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/AbstractExecutorServiceTest.java
@@ -0,0 +1,635 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.security.PrivilegedAction;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.AbstractExecutorService;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AbstractExecutorServiceTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AbstractExecutorServiceTest.class);
+    }
+
+    /**
+     * A no-frills implementation of AbstractExecutorService, designed
+     * to test the submit methods only.
+     */
+    static class DirectExecutorService extends AbstractExecutorService {
+        public void execute(Runnable r) { r.run(); }
+        public void shutdown() { shutdown = true; }
+        public List<Runnable> shutdownNow() {
+            shutdown = true;
+            return Collections.EMPTY_LIST;
+        }
+        public boolean isShutdown() { return shutdown; }
+        public boolean isTerminated() { return isShutdown(); }
+        public boolean awaitTermination(long timeout, TimeUnit unit) {
+            return isShutdown();
+        }
+        private volatile boolean shutdown = false;
+    }
+
+    /**
+     * execute(runnable) runs it to completion
+     */
+    public void testExecuteRunnable() throws Exception {
+        ExecutorService e = new DirectExecutorService();
+        final AtomicBoolean done = new AtomicBoolean(false);
+        Future<?> future = e.submit(new CheckedRunnable() {
+            public void realRun() {
+                done.set(true);
+            }});
+        assertNull(future.get());
+        assertNull(future.get(0, MILLISECONDS));
+        assertTrue(done.get());
+        assertTrue(future.isDone());
+        assertFalse(future.isCancelled());
+    }
+
+    /**
+     * Completed submit(callable) returns result
+     */
+    public void testSubmitCallable() throws Exception {
+        ExecutorService e = new DirectExecutorService();
+        Future<String> future = e.submit(new StringTask());
+        String result = future.get();
+        assertSame(TEST_STRING, result);
+    }
+
+    /**
+     * Completed submit(runnable) returns successfully
+     */
+    public void testSubmitRunnable() throws Exception {
+        ExecutorService e = new DirectExecutorService();
+        Future<?> future = e.submit(new NoOpRunnable());
+        future.get();
+        assertTrue(future.isDone());
+    }
+
+    /**
+     * Completed submit(runnable, result) returns result
+     */
+    public void testSubmitRunnable2() throws Exception {
+        ExecutorService e = new DirectExecutorService();
+        Future<String> future = e.submit(new NoOpRunnable(), TEST_STRING);
+        String result = future.get();
+        assertSame(TEST_STRING, result);
+    }
+
+    /**
+     * A submitted privileged action runs to completion
+     */
+    public void testSubmitPrivilegedAction() throws Exception {
+        Runnable r = new CheckedRunnable() {
+            public void realRun() throws Exception {
+                ExecutorService e = new DirectExecutorService();
+                Future future = e.submit(Executors.callable(new PrivilegedAction() {
+                    public Object run() {
+                        return TEST_STRING;
+                    }}));
+
+                assertSame(TEST_STRING, future.get());
+            }};
+
+        runWithPermissions(r,
+                           new RuntimePermission("getClassLoader"),
+                           new RuntimePermission("setContextClassLoader"),
+                           new RuntimePermission("modifyThread"));
+    }
+
+    /**
+     * A submitted privileged exception action runs to completion
+     */
+    public void testSubmitPrivilegedExceptionAction() throws Exception {
+        Runnable r = new CheckedRunnable() {
+            public void realRun() throws Exception {
+                ExecutorService e = new DirectExecutorService();
+                Future future = e.submit(Executors.callable(new PrivilegedExceptionAction() {
+                    public Object run() {
+                        return TEST_STRING;
+                    }}));
+
+                assertSame(TEST_STRING, future.get());
+            }};
+
+        runWithPermissions(r);
+    }
+
+    /**
+     * A submitted failed privileged exception action reports exception
+     */
+    public void testSubmitFailedPrivilegedExceptionAction() throws Exception {
+        Runnable r = new CheckedRunnable() {
+            public void realRun() throws Exception {
+                ExecutorService e = new DirectExecutorService();
+                Future future = e.submit(Executors.callable(new PrivilegedExceptionAction() {
+                    public Object run() throws Exception {
+                        throw new IndexOutOfBoundsException();
+                    }}));
+
+                try {
+                    future.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    assertTrue(success.getCause() instanceof IndexOutOfBoundsException);
+                }}};
+
+        runWithPermissions(r);
+    }
+
+    /**
+     * execute(null runnable) throws NPE
+     */
+    public void testExecuteNullRunnable() {
+        ExecutorService e = new DirectExecutorService();
+        try {
+            e.submit((Runnable) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * submit(null callable) throws NPE
+     */
+    public void testSubmitNullCallable() {
+        ExecutorService e = new DirectExecutorService();
+        try {
+            e.submit((Callable) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * submit(callable).get() throws InterruptedException if interrupted
+     */
+    public void testInterruptedSubmit() throws InterruptedException {
+        final CountDownLatch submitted    = new CountDownLatch(1);
+        final CountDownLatch quittingTime = new CountDownLatch(1);
+        final Callable<Void> awaiter = new CheckedCallable<Void>() {
+            public Void realCall() throws InterruptedException {
+                assertTrue(quittingTime.await(2*LONG_DELAY_MS, MILLISECONDS));
+                return null;
+            }};
+        final ExecutorService p
+            = new ThreadPoolExecutor(1,1,60, TimeUnit.SECONDS,
+                                     new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p, quittingTime)) {
+            Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+                public void realRun() throws Exception {
+                    Future<Void> future = p.submit(awaiter);
+                    submitted.countDown();
+                    future.get();
+                }});
+
+            await(submitted);
+            t.interrupt();
+            awaitTermination(t);
+        }
+    }
+
+    /**
+     * get of submit(callable) throws ExecutionException if callable
+     * throws exception
+     */
+    public void testSubmitEE() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   60, TimeUnit.SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            Callable c = new Callable() {
+                public Object call() { throw new ArithmeticException(); }};
+            try {
+                p.submit(c).get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof ArithmeticException);
+            }
+        }
+    }
+
+    /**
+     * invokeAny(null) throws NPE
+     */
+    public void testInvokeAny1() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * invokeAny(empty collection) throws IAE
+     */
+    public void testInvokeAny2() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(new ArrayList<Callable<String>>());
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * invokeAny(c) throws NPE if c has null elements
+     */
+    public void testInvokeAny3() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<Long>> l = new ArrayList<Callable<Long>>();
+            l.add(new Callable<Long>() {
+                      public Long call() { throw new ArithmeticException(); }});
+            l.add(null);
+            try {
+                e.invokeAny(l);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * invokeAny(c) throws ExecutionException if no task in c completes
+     */
+    public void testInvokeAny4() throws InterruptedException {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new NPETask());
+            try {
+                e.invokeAny(l);
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * invokeAny(c) returns result of some task in c if at least one completes
+     */
+    public void testInvokeAny5() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l);
+            assertSame(TEST_STRING, result);
+        }
+    }
+
+    /**
+     * invokeAll(null) throws NPE
+     */
+    public void testInvokeAll1() throws InterruptedException {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAll(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * invokeAll(empty collection) returns empty collection
+     */
+    public void testInvokeAll2() throws InterruptedException {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>());
+            assertTrue(r.isEmpty());
+        }
+    }
+
+    /**
+     * invokeAll(c) throws NPE if c has null elements
+     */
+    public void testInvokeAll3() throws InterruptedException {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(null);
+            try {
+                e.invokeAll(l);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * get of returned element of invokeAll(c) throws exception on failed task
+     */
+    public void testInvokeAll4() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new NPETask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(1, futures.size());
+            try {
+                futures.get(0).get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * invokeAll(c) returns results of all completed tasks in c
+     */
+    public void testInvokeAll5() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        }
+    }
+
+    /**
+     * timed invokeAny(null) throws NPE
+     */
+    public void testTimedInvokeAny1() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(null time unit) throws NPE
+     */
+    public void testTimedInvokeAnyNullTimeUnit() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            try {
+                e.invokeAny(l, MEDIUM_DELAY_MS, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(empty collection) throws IAE
+     */
+    public void testTimedInvokeAny2() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(new ArrayList<Callable<String>>(),
+                            MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAny3() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<Long>> l = new ArrayList<Callable<Long>>();
+            l.add(new Callable<Long>() {
+                      public Long call() { throw new ArithmeticException(); }});
+            l.add(null);
+            try {
+                e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testTimedInvokeAny4() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            long startTime = System.nanoTime();
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new NPETask());
+            try {
+                e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) returns result of some task in c
+     */
+    public void testTimedInvokeAny5() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            long startTime = System.nanoTime();
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+            assertSame(TEST_STRING, result);
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * timed invokeAll(null) throws NPE
+     */
+    public void testTimedInvokeAll1() throws InterruptedException {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAll(null time unit) throws NPE
+     */
+    public void testTimedInvokeAllNullTimeUnit() throws InterruptedException {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            try {
+                e.invokeAll(l, MEDIUM_DELAY_MS, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAll(empty collection) returns empty collection
+     */
+    public void testTimedInvokeAll2() throws InterruptedException {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
+            assertTrue(r.isEmpty());
+        }
+    }
+
+    /**
+     * timed invokeAll(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAll3() throws InterruptedException {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(null);
+            try {
+                e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * get of returned element of invokeAll(c) throws exception on failed task
+     */
+    public void testTimedInvokeAll4() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new NPETask());
+            List<Future<String>> futures =
+                e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
+            assertEquals(1, futures.size());
+            try {
+                futures.get(0).get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * timed invokeAll(c) returns results of all completed tasks in c
+     */
+    public void testTimedInvokeAll5() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures =
+                e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        }
+    }
+
+    /**
+     * timed invokeAll cancels tasks not completed by timeout
+     */
+    public void testTimedInvokeAll6() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            for (long timeout = timeoutMillis();;) {
+                List<Callable<String>> tasks = new ArrayList<>();
+                tasks.add(new StringTask("0"));
+                tasks.add(Executors.callable(possiblyInterruptedRunnable(timeout),
+                                             TEST_STRING));
+                tasks.add(new StringTask("2"));
+                long startTime = System.nanoTime();
+                List<Future<String>> futures =
+                    e.invokeAll(tasks, timeout, MILLISECONDS);
+                assertEquals(tasks.size(), futures.size());
+                assertTrue(millisElapsedSince(startTime) >= timeout);
+                for (Future future : futures)
+                    assertTrue(future.isDone());
+                try {
+                    assertEquals("0", futures.get(0).get());
+                    assertEquals(TEST_STRING, futures.get(1).get());
+                } catch (CancellationException retryWithLongerTimeout) {
+                    // unusual delay before starting second task
+                    timeout *= 2;
+                    if (timeout >= LONG_DELAY_MS / 2)
+                        fail("expected exactly one task to be cancelled");
+                    continue;
+                }
+                assertTrue(futures.get(2).isCancelled());
+                break;
+            }
+        }
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/AbstractQueueTest.java b/jdk/test/java/util/concurrent/tck/AbstractQueueTest.java
new file mode 100644
index 00000000000..ddc769d3a45
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/AbstractQueueTest.java
@@ -0,0 +1,205 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import java.util.AbstractQueue;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AbstractQueueTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AbstractQueueTest.class);
+    }
+
+    static class Succeed extends AbstractQueue<Integer> {
+        public boolean offer(Integer x) {
+            if (x == null) throw new NullPointerException();
+            return true;
+        }
+        public Integer peek() { return one; }
+        public Integer poll() { return one; }
+        public int size() { return 0; }
+        public Iterator iterator() { return null; } // not needed
+    }
+
+    static class Fail extends AbstractQueue<Integer> {
+        public boolean offer(Integer x) {
+            if (x == null) throw new NullPointerException();
+            return false;
+        }
+        public Integer peek() { return null; }
+        public Integer poll() { return null; }
+        public int size() { return 0; }
+        public Iterator iterator() { return null; } // not needed
+    }
+
+    /**
+     * add returns true if offer succeeds
+     */
+    public void testAddS() {
+        Succeed q = new Succeed();
+        assertTrue(q.add(two));
+    }
+
+    /**
+     * add throws ISE true if offer fails
+     */
+    public void testAddF() {
+        Fail q = new Fail();
+        try {
+            q.add(one);
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * add throws NPE if offer does
+     */
+    public void testAddNPE() {
+        Succeed q = new Succeed();
+        try {
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove returns normally if poll succeeds
+     */
+    public void testRemoveS() {
+        Succeed q = new Succeed();
+        q.remove();
+    }
+
+    /**
+     * remove throws NSEE if poll returns null
+     */
+    public void testRemoveF() {
+        Fail q = new Fail();
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * element returns normally if peek succeeds
+     */
+    public void testElementS() {
+        Succeed q = new Succeed();
+        q.element();
+    }
+
+    /**
+     * element throws NSEE if peek returns null
+     */
+    public void testElementF() {
+        Fail q = new Fail();
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        Succeed q = new Succeed();
+        try {
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll(this) throws IAE
+     */
+    public void testAddAllSelf() {
+        Succeed q = new Succeed();
+        try {
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testAddAll2() {
+        Succeed q = new Succeed();
+        Integer[] ints = new Integer[SIZE];
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        Succeed q = new Succeed();
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll throws ISE if an add fails
+     */
+    public void testAddAll4() {
+        Fail q = new Fail();
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/AbstractQueuedLongSynchronizerTest.java b/jdk/test/java/util/concurrent/tck/AbstractQueuedLongSynchronizerTest.java
new file mode 100644
index 00000000000..88bdd13b4dc
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/AbstractQueuedLongSynchronizerTest.java
@@ -0,0 +1,1280 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.concurrent.locks.AbstractQueuedLongSynchronizer;
+import java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AbstractQueuedLongSynchronizerTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AbstractQueuedLongSynchronizerTest.class);
+    }
+
+    /**
+     * A simple mutex class, adapted from the class javadoc.  Exclusive
+     * acquire tests exercise this as a sample user extension.
+     */
+    static class Mutex extends AbstractQueuedLongSynchronizer {
+        /** An eccentric value > 32 bits for locked synchronizer state. */
+        static final long LOCKED = (1L << 63) | (1L << 15);
+
+        static final long UNLOCKED = 0;
+
+        public boolean isHeldExclusively() {
+            long state = getState();
+            assertTrue(state == UNLOCKED || state == LOCKED);
+            return state == LOCKED;
+        }
+
+        public boolean tryAcquire(long acquires) {
+            assertEquals(LOCKED, acquires);
+            return compareAndSetState(UNLOCKED, LOCKED);
+        }
+
+        public boolean tryRelease(long releases) {
+            if (getState() != LOCKED) throw new IllegalMonitorStateException();
+            setState(UNLOCKED);
+            return true;
+        }
+
+        public boolean tryAcquireNanos(long nanos) throws InterruptedException {
+            return tryAcquireNanos(LOCKED, nanos);
+        }
+
+        public boolean tryAcquire() {
+            return tryAcquire(LOCKED);
+        }
+
+        public boolean tryRelease() {
+            return tryRelease(LOCKED);
+        }
+
+        public void acquire() {
+            acquire(LOCKED);
+        }
+
+        public void acquireInterruptibly() throws InterruptedException {
+            acquireInterruptibly(LOCKED);
+        }
+
+        public void release() {
+            release(LOCKED);
+        }
+
+        public ConditionObject newCondition() {
+            return new ConditionObject();
+        }
+    }
+
+    /**
+     * A simple latch class, to test shared mode.
+     */
+    static class BooleanLatch extends AbstractQueuedLongSynchronizer {
+        public boolean isSignalled() { return getState() != 0; }
+
+        public long tryAcquireShared(long ignore) {
+            return isSignalled() ? 1 : -1;
+        }
+
+        public boolean tryReleaseShared(long ignore) {
+            setState(1L << 62);
+            return true;
+        }
+    }
+
+    /**
+     * A runnable calling acquireInterruptibly that does not expect to
+     * be interrupted.
+     */
+    class InterruptibleSyncRunnable extends CheckedRunnable {
+        final Mutex sync;
+        InterruptibleSyncRunnable(Mutex sync) { this.sync = sync; }
+        public void realRun() throws InterruptedException {
+            sync.acquireInterruptibly();
+        }
+    }
+
+    /**
+     * A runnable calling acquireInterruptibly that expects to be
+     * interrupted.
+     */
+    class InterruptedSyncRunnable extends CheckedInterruptedRunnable {
+        final Mutex sync;
+        InterruptedSyncRunnable(Mutex sync) { this.sync = sync; }
+        public void realRun() throws InterruptedException {
+            sync.acquireInterruptibly();
+        }
+    }
+
+    /** A constant to clarify calls to checking methods below. */
+    static final Thread[] NO_THREADS = new Thread[0];
+
+    /**
+     * Spin-waits until sync.isQueued(t) becomes true.
+     */
+    void waitForQueuedThread(AbstractQueuedLongSynchronizer sync,
+                             Thread t) {
+        long startTime = System.nanoTime();
+        while (!sync.isQueued(t)) {
+            if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                throw new AssertionFailedError("timed out");
+            Thread.yield();
+        }
+        assertTrue(t.isAlive());
+    }
+
+    /**
+     * Checks that sync has exactly the given queued threads.
+     */
+    void assertHasQueuedThreads(AbstractQueuedLongSynchronizer sync,
+                                Thread... expected) {
+        Collection<Thread> actual = sync.getQueuedThreads();
+        assertEquals(expected.length > 0, sync.hasQueuedThreads());
+        assertEquals(expected.length, sync.getQueueLength());
+        assertEquals(expected.length, actual.size());
+        assertEquals(expected.length == 0, actual.isEmpty());
+        assertEquals(new HashSet<Thread>(actual),
+                     new HashSet<Thread>(Arrays.asList(expected)));
+    }
+
+    /**
+     * Checks that sync has exactly the given (exclusive) queued threads.
+     */
+    void assertHasExclusiveQueuedThreads(AbstractQueuedLongSynchronizer sync,
+                                         Thread... expected) {
+        assertHasQueuedThreads(sync, expected);
+        assertEquals(new HashSet<Thread>(sync.getExclusiveQueuedThreads()),
+                     new HashSet<Thread>(sync.getQueuedThreads()));
+        assertEquals(0, sync.getSharedQueuedThreads().size());
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+    }
+
+    /**
+     * Checks that sync has exactly the given (shared) queued threads.
+     */
+    void assertHasSharedQueuedThreads(AbstractQueuedLongSynchronizer sync,
+                                      Thread... expected) {
+        assertHasQueuedThreads(sync, expected);
+        assertEquals(new HashSet<Thread>(sync.getSharedQueuedThreads()),
+                     new HashSet<Thread>(sync.getQueuedThreads()));
+        assertEquals(0, sync.getExclusiveQueuedThreads().size());
+        assertTrue(sync.getExclusiveQueuedThreads().isEmpty());
+    }
+
+    /**
+     * Checks that condition c has exactly the given waiter threads,
+     * after acquiring mutex.
+     */
+    void assertHasWaitersUnlocked(Mutex sync, ConditionObject c,
+                                 Thread... threads) {
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, threads);
+        sync.release();
+    }
+
+    /**
+     * Checks that condition c has exactly the given waiter threads.
+     */
+    void assertHasWaitersLocked(Mutex sync, ConditionObject c,
+                                Thread... threads) {
+        assertEquals(threads.length > 0, sync.hasWaiters(c));
+        assertEquals(threads.length, sync.getWaitQueueLength(c));
+        assertEquals(threads.length == 0, sync.getWaitingThreads(c).isEmpty());
+        assertEquals(threads.length, sync.getWaitingThreads(c).size());
+        assertEquals(new HashSet<Thread>(sync.getWaitingThreads(c)),
+                     new HashSet<Thread>(Arrays.asList(threads)));
+    }
+
+    enum AwaitMethod { await, awaitTimed, awaitNanos, awaitUntil }
+
+    /**
+     * Awaits condition using the specified AwaitMethod.
+     */
+    void await(ConditionObject c, AwaitMethod awaitMethod)
+            throws InterruptedException {
+        long timeoutMillis = 2 * LONG_DELAY_MS;
+        switch (awaitMethod) {
+        case await:
+            c.await();
+            break;
+        case awaitTimed:
+            assertTrue(c.await(timeoutMillis, MILLISECONDS));
+            break;
+        case awaitNanos:
+            long nanosTimeout = MILLISECONDS.toNanos(timeoutMillis);
+            long nanosRemaining = c.awaitNanos(nanosTimeout);
+            assertTrue(nanosRemaining > 0);
+            break;
+        case awaitUntil:
+            assertTrue(c.awaitUntil(delayedDate(timeoutMillis)));
+            break;
+        default:
+            throw new AssertionError();
+        }
+    }
+
+    /**
+     * Checks that awaiting the given condition times out (using the
+     * default timeout duration).
+     */
+    void assertAwaitTimesOut(ConditionObject c, AwaitMethod awaitMethod) {
+        long timeoutMillis = timeoutMillis();
+        long startTime;
+        try {
+            switch (awaitMethod) {
+            case awaitTimed:
+                startTime = System.nanoTime();
+                assertFalse(c.await(timeoutMillis, MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+                break;
+            case awaitNanos:
+                startTime = System.nanoTime();
+                long nanosTimeout = MILLISECONDS.toNanos(timeoutMillis);
+                long nanosRemaining = c.awaitNanos(nanosTimeout);
+                assertTrue(nanosRemaining <= 0);
+                assertTrue(nanosRemaining > -MILLISECONDS.toNanos(LONG_DELAY_MS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+                break;
+            case awaitUntil:
+                // We shouldn't assume that nanoTime and currentTimeMillis
+                // use the same time source, so don't use nanoTime here.
+                java.util.Date delayedDate = delayedDate(timeoutMillis());
+                assertFalse(c.awaitUntil(delayedDate(timeoutMillis)));
+                assertTrue(new java.util.Date().getTime() >= delayedDate.getTime());
+                break;
+            default:
+                throw new UnsupportedOperationException();
+            }
+        } catch (InterruptedException ie) { threadUnexpectedException(ie); }
+    }
+
+    /**
+     * isHeldExclusively is false upon construction
+     */
+    public void testIsHeldExclusively() {
+        Mutex sync = new Mutex();
+        assertFalse(sync.isHeldExclusively());
+    }
+
+    /**
+     * acquiring released sync succeeds
+     */
+    public void testAcquire() {
+        Mutex sync = new Mutex();
+        sync.acquire();
+        assertTrue(sync.isHeldExclusively());
+        sync.release();
+        assertFalse(sync.isHeldExclusively());
+    }
+
+    /**
+     * tryAcquire on a released sync succeeds
+     */
+    public void testTryAcquire() {
+        Mutex sync = new Mutex();
+        assertTrue(sync.tryAcquire());
+        assertTrue(sync.isHeldExclusively());
+        sync.release();
+        assertFalse(sync.isHeldExclusively());
+    }
+
+    /**
+     * hasQueuedThreads reports whether there are waiting threads
+     */
+    public void testHasQueuedThreads() {
+        final Mutex sync = new Mutex();
+        assertFalse(sync.hasQueuedThreads());
+        sync.acquire();
+        Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t1);
+        assertTrue(sync.hasQueuedThreads());
+        Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync));
+        waitForQueuedThread(sync, t2);
+        assertTrue(sync.hasQueuedThreads());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertTrue(sync.hasQueuedThreads());
+        sync.release();
+        awaitTermination(t2);
+        assertFalse(sync.hasQueuedThreads());
+    }
+
+    /**
+     * isQueued(null) throws NullPointerException
+     */
+    public void testIsQueuedNPE() {
+        final Mutex sync = new Mutex();
+        try {
+            sync.isQueued(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * isQueued reports whether a thread is queued
+     */
+    public void testIsQueued() {
+        final Mutex sync = new Mutex();
+        Thread t1 = new Thread(new InterruptedSyncRunnable(sync));
+        Thread t2 = new Thread(new InterruptibleSyncRunnable(sync));
+        assertFalse(sync.isQueued(t1));
+        assertFalse(sync.isQueued(t2));
+        sync.acquire();
+        t1.start();
+        waitForQueuedThread(sync, t1);
+        assertTrue(sync.isQueued(t1));
+        assertFalse(sync.isQueued(t2));
+        t2.start();
+        waitForQueuedThread(sync, t2);
+        assertTrue(sync.isQueued(t1));
+        assertTrue(sync.isQueued(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertFalse(sync.isQueued(t1));
+        assertTrue(sync.isQueued(t2));
+        sync.release();
+        awaitTermination(t2);
+        assertFalse(sync.isQueued(t1));
+        assertFalse(sync.isQueued(t2));
+    }
+
+    /**
+     * getFirstQueuedThread returns first waiting thread or null if none
+     */
+    public void testGetFirstQueuedThread() {
+        final Mutex sync = new Mutex();
+        assertNull(sync.getFirstQueuedThread());
+        sync.acquire();
+        Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t1);
+        assertEquals(t1, sync.getFirstQueuedThread());
+        Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync));
+        waitForQueuedThread(sync, t2);
+        assertEquals(t1, sync.getFirstQueuedThread());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertEquals(t2, sync.getFirstQueuedThread());
+        sync.release();
+        awaitTermination(t2);
+        assertNull(sync.getFirstQueuedThread());
+    }
+
+    /**
+     * hasContended reports false if no thread has ever blocked, else true
+     */
+    public void testHasContended() {
+        final Mutex sync = new Mutex();
+        assertFalse(sync.hasContended());
+        sync.acquire();
+        assertFalse(sync.hasContended());
+        Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t1);
+        assertTrue(sync.hasContended());
+        Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync));
+        waitForQueuedThread(sync, t2);
+        assertTrue(sync.hasContended());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertTrue(sync.hasContended());
+        sync.release();
+        awaitTermination(t2);
+        assertTrue(sync.hasContended());
+    }
+
+    /**
+     * getQueuedThreads returns all waiting threads
+     */
+    public void testGetQueuedThreads() {
+        final Mutex sync = new Mutex();
+        Thread t1 = new Thread(new InterruptedSyncRunnable(sync));
+        Thread t2 = new Thread(new InterruptibleSyncRunnable(sync));
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        sync.acquire();
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        t1.start();
+        waitForQueuedThread(sync, t1);
+        assertHasExclusiveQueuedThreads(sync, t1);
+        assertTrue(sync.getQueuedThreads().contains(t1));
+        assertFalse(sync.getQueuedThreads().contains(t2));
+        t2.start();
+        waitForQueuedThread(sync, t2);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        assertTrue(sync.getQueuedThreads().contains(t1));
+        assertTrue(sync.getQueuedThreads().contains(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertHasExclusiveQueuedThreads(sync, t2);
+        sync.release();
+        awaitTermination(t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+    }
+
+    /**
+     * getExclusiveQueuedThreads returns all exclusive waiting threads
+     */
+    public void testGetExclusiveQueuedThreads() {
+        final Mutex sync = new Mutex();
+        Thread t1 = new Thread(new InterruptedSyncRunnable(sync));
+        Thread t2 = new Thread(new InterruptibleSyncRunnable(sync));
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        sync.acquire();
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        t1.start();
+        waitForQueuedThread(sync, t1);
+        assertHasExclusiveQueuedThreads(sync, t1);
+        assertTrue(sync.getExclusiveQueuedThreads().contains(t1));
+        assertFalse(sync.getExclusiveQueuedThreads().contains(t2));
+        t2.start();
+        waitForQueuedThread(sync, t2);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        assertTrue(sync.getExclusiveQueuedThreads().contains(t1));
+        assertTrue(sync.getExclusiveQueuedThreads().contains(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertHasExclusiveQueuedThreads(sync, t2);
+        sync.release();
+        awaitTermination(t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+    }
+
+    /**
+     * getSharedQueuedThreads does not include exclusively waiting threads
+     */
+    public void testGetSharedQueuedThreads_Exclusive() {
+        final Mutex sync = new Mutex();
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        sync.acquire();
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t1);
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync));
+        waitForQueuedThread(sync, t2);
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        sync.release();
+        awaitTermination(t2);
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+    }
+
+    /**
+     * getSharedQueuedThreads returns all shared waiting threads
+     */
+    public void testGetSharedQueuedThreads_Shared() {
+        final BooleanLatch l = new BooleanLatch();
+        assertHasSharedQueuedThreads(l, NO_THREADS);
+        Thread t1 = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                l.acquireSharedInterruptibly(0);
+            }});
+        waitForQueuedThread(l, t1);
+        assertHasSharedQueuedThreads(l, t1);
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                l.acquireSharedInterruptibly(0);
+            }});
+        waitForQueuedThread(l, t2);
+        assertHasSharedQueuedThreads(l, t1, t2);
+        t1.interrupt();
+        awaitTermination(t1);
+        assertHasSharedQueuedThreads(l, t2);
+        assertTrue(l.releaseShared(0));
+        awaitTermination(t2);
+        assertHasSharedQueuedThreads(l, NO_THREADS);
+    }
+
+    /**
+     * tryAcquireNanos is interruptible
+     */
+    public void testTryAcquireNanos_Interruptible() {
+        final Mutex sync = new Mutex();
+        sync.acquire();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.tryAcquireNanos(MILLISECONDS.toNanos(2 * LONG_DELAY_MS));
+            }});
+
+        waitForQueuedThread(sync, t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * tryAcquire on exclusively held sync fails
+     */
+    public void testTryAcquireWhenSynced() {
+        final Mutex sync = new Mutex();
+        sync.acquire();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertFalse(sync.tryAcquire());
+            }});
+
+        awaitTermination(t);
+        sync.release();
+    }
+
+    /**
+     * tryAcquireNanos on an exclusively held sync times out
+     */
+    public void testAcquireNanos_Timeout() {
+        final Mutex sync = new Mutex();
+        sync.acquire();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                long nanos = MILLISECONDS.toNanos(timeoutMillis());
+                assertFalse(sync.tryAcquireNanos(nanos));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            }});
+
+        awaitTermination(t);
+        sync.release();
+    }
+
+    /**
+     * getState is true when acquired and false when not
+     */
+    public void testGetState() {
+        final Mutex sync = new Mutex();
+        sync.acquire();
+        assertTrue(sync.isHeldExclusively());
+        sync.release();
+        assertFalse(sync.isHeldExclusively());
+
+        final BooleanLatch acquired = new BooleanLatch();
+        final BooleanLatch done = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertTrue(acquired.releaseShared(0));
+                done.acquireShared(0);
+                sync.release();
+            }});
+
+        acquired.acquireShared(0);
+        assertTrue(sync.isHeldExclusively());
+        assertTrue(done.releaseShared(0));
+        awaitTermination(t);
+        assertFalse(sync.isHeldExclusively());
+    }
+
+    /**
+     * acquireInterruptibly succeeds when released, else is interruptible
+     */
+    public void testAcquireInterruptibly() throws InterruptedException {
+        final Mutex sync = new Mutex();
+        final BooleanLatch threadStarted = new BooleanLatch();
+        sync.acquireInterruptibly();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertTrue(threadStarted.releaseShared(0));
+                sync.acquireInterruptibly();
+            }});
+
+        threadStarted.acquireShared(0);
+        waitForQueuedThread(sync, t);
+        t.interrupt();
+        awaitTermination(t);
+        assertTrue(sync.isHeldExclusively());
+    }
+
+    /**
+     * owns is true for a condition created by sync else false
+     */
+    public void testOwns() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final Mutex sync2 = new Mutex();
+        assertTrue(sync.owns(c));
+        assertFalse(sync2.owns(c));
+    }
+
+    /**
+     * Calling await without holding sync throws IllegalMonitorStateException
+     */
+    public void testAwait_IMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        for (AwaitMethod awaitMethod : AwaitMethod.values()) {
+            long startTime = System.nanoTime();
+            try {
+                await(c, awaitMethod);
+                shouldThrow();
+            } catch (IllegalMonitorStateException success) {
+            } catch (InterruptedException e) { threadUnexpectedException(e); }
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * Calling signal without holding sync throws IllegalMonitorStateException
+     */
+    public void testSignal_IMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            c.signal();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * Calling signalAll without holding sync throws IllegalMonitorStateException
+     */
+    public void testSignalAll_IMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            c.signalAll();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * await/awaitNanos/awaitUntil without a signal times out
+     */
+    public void testAwaitTimed_Timeout() { testAwait_Timeout(AwaitMethod.awaitTimed); }
+    public void testAwaitNanos_Timeout() { testAwait_Timeout(AwaitMethod.awaitNanos); }
+    public void testAwaitUntil_Timeout() { testAwait_Timeout(AwaitMethod.awaitUntil); }
+    public void testAwait_Timeout(AwaitMethod awaitMethod) {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        sync.acquire();
+        assertAwaitTimesOut(c, awaitMethod);
+        sync.release();
+    }
+
+    /**
+     * await/awaitNanos/awaitUntil returns when signalled
+     */
+    public void testSignal_await()      { testSignal(AwaitMethod.await); }
+    public void testSignal_awaitTimed() { testSignal(AwaitMethod.awaitTimed); }
+    public void testSignal_awaitNanos() { testSignal(AwaitMethod.awaitNanos); }
+    public void testSignal_awaitUntil() { testSignal(AwaitMethod.awaitUntil); }
+    public void testSignal(final AwaitMethod awaitMethod) {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertTrue(acquired.releaseShared(0));
+                await(c, awaitMethod);
+                sync.release();
+            }});
+
+        acquired.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        c.signal();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t);
+        sync.release();
+        awaitTermination(t);
+    }
+
+    /**
+     * hasWaiters(null) throws NullPointerException
+     */
+    public void testHasWaitersNPE() {
+        final Mutex sync = new Mutex();
+        try {
+            sync.hasWaiters(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * getWaitQueueLength(null) throws NullPointerException
+     */
+    public void testGetWaitQueueLengthNPE() {
+        final Mutex sync = new Mutex();
+        try {
+            sync.getWaitQueueLength(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * getWaitingThreads throws NPE if null
+     */
+    public void testGetWaitingThreadsNPE() {
+        final Mutex sync = new Mutex();
+        try {
+            sync.getWaitingThreads(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * hasWaiters throws IllegalArgumentException if not owned
+     */
+    public void testHasWaitersIAE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final Mutex sync2 = new Mutex();
+        try {
+            sync2.hasWaiters(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * hasWaiters throws IllegalMonitorStateException if not synced
+     */
+    public void testHasWaitersIMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            sync.hasWaiters(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitQueueLength throws IllegalArgumentException if not owned
+     */
+    public void testGetWaitQueueLengthIAE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final Mutex sync2 = new Mutex();
+        try {
+            sync2.getWaitQueueLength(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitQueueLength throws IllegalMonitorStateException if not synced
+     */
+    public void testGetWaitQueueLengthIMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            sync.getWaitQueueLength(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitingThreads throws IllegalArgumentException if not owned
+     */
+    public void testGetWaitingThreadsIAE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final Mutex sync2 = new Mutex();
+        try {
+            sync2.getWaitingThreads(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitingThreads throws IllegalMonitorStateException if not synced
+     */
+    public void testGetWaitingThreadsIMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            sync.getWaitingThreads(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * hasWaiters returns true when a thread is waiting, else false
+     */
+    public void testHasWaiters() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, NO_THREADS);
+                assertFalse(sync.hasWaiters(c));
+                assertTrue(acquired.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+
+        acquired.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        assertTrue(sync.hasWaiters(c));
+        c.signal();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t);
+        assertFalse(sync.hasWaiters(c));
+        sync.release();
+
+        awaitTermination(t);
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitQueueLength returns number of waiting threads
+     */
+    public void testGetWaitQueueLength() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired1 = new BooleanLatch();
+        final BooleanLatch acquired2 = new BooleanLatch();
+        final Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, NO_THREADS);
+                assertEquals(0, sync.getWaitQueueLength(c));
+                assertTrue(acquired1.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+        acquired1.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1);
+        assertEquals(1, sync.getWaitQueueLength(c));
+        sync.release();
+
+        final Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, t1);
+                assertEquals(1, sync.getWaitQueueLength(c));
+                assertTrue(acquired2.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+        acquired2.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1, t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        assertEquals(2, sync.getWaitQueueLength(c));
+        c.signalAll();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        assertEquals(0, sync.getWaitQueueLength(c));
+        sync.release();
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitingThreads returns only and all waiting threads
+     */
+    public void testGetWaitingThreads() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired1 = new BooleanLatch();
+        final BooleanLatch acquired2 = new BooleanLatch();
+        final Thread t1 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, NO_THREADS);
+                assertTrue(sync.getWaitingThreads(c).isEmpty());
+                assertTrue(acquired1.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+
+        final Thread t2 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, t1);
+                assertTrue(sync.getWaitingThreads(c).contains(t1));
+                assertFalse(sync.getWaitingThreads(c).isEmpty());
+                assertEquals(1, sync.getWaitingThreads(c).size());
+                assertTrue(acquired2.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertFalse(sync.getWaitingThreads(c).contains(t1));
+        assertFalse(sync.getWaitingThreads(c).contains(t2));
+        assertTrue(sync.getWaitingThreads(c).isEmpty());
+        assertEquals(0, sync.getWaitingThreads(c).size());
+        sync.release();
+
+        t1.start();
+        acquired1.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1);
+        assertTrue(sync.getWaitingThreads(c).contains(t1));
+        assertFalse(sync.getWaitingThreads(c).contains(t2));
+        assertFalse(sync.getWaitingThreads(c).isEmpty());
+        assertEquals(1, sync.getWaitingThreads(c).size());
+        sync.release();
+
+        t2.start();
+        acquired2.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1, t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        assertTrue(sync.getWaitingThreads(c).contains(t1));
+        assertTrue(sync.getWaitingThreads(c).contains(t2));
+        assertFalse(sync.getWaitingThreads(c).isEmpty());
+        assertEquals(2, sync.getWaitingThreads(c).size());
+        c.signalAll();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        assertFalse(sync.getWaitingThreads(c).contains(t1));
+        assertFalse(sync.getWaitingThreads(c).contains(t2));
+        assertTrue(sync.getWaitingThreads(c).isEmpty());
+        assertEquals(0, sync.getWaitingThreads(c).size());
+        sync.release();
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * awaitUninterruptibly is uninterruptible
+     */
+    public void testAwaitUninterruptibly() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch pleaseInterrupt = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                sync.acquire();
+                assertTrue(pleaseInterrupt.releaseShared(0));
+                c.awaitUninterruptibly();
+                assertTrue(Thread.interrupted());
+                assertHasWaitersLocked(sync, c, NO_THREADS);
+                sync.release();
+            }});
+
+        pleaseInterrupt.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t);
+        sync.release();
+        t.interrupt();
+        assertHasWaitersUnlocked(sync, c, t);
+        assertThreadStaysAlive(t);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        c.signal();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t);
+        sync.release();
+        awaitTermination(t);
+    }
+
+    /**
+     * await/awaitNanos/awaitUntil is interruptible
+     */
+    public void testInterruptible_await()      { testInterruptible(AwaitMethod.await); }
+    public void testInterruptible_awaitTimed() { testInterruptible(AwaitMethod.awaitTimed); }
+    public void testInterruptible_awaitNanos() { testInterruptible(AwaitMethod.awaitNanos); }
+    public void testInterruptible_awaitUntil() { testInterruptible(AwaitMethod.awaitUntil); }
+    public void testInterruptible(final AwaitMethod awaitMethod) {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch pleaseInterrupt = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertTrue(pleaseInterrupt.releaseShared(0));
+                await(c, awaitMethod);
+            }});
+
+        pleaseInterrupt.acquireShared(0);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * signalAll wakes up all threads
+     */
+    public void testSignalAll_await()      { testSignalAll(AwaitMethod.await); }
+    public void testSignalAll_awaitTimed() { testSignalAll(AwaitMethod.awaitTimed); }
+    public void testSignalAll_awaitNanos() { testSignalAll(AwaitMethod.awaitNanos); }
+    public void testSignalAll_awaitUntil() { testSignalAll(AwaitMethod.awaitUntil); }
+    public void testSignalAll(final AwaitMethod awaitMethod) {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired1 = new BooleanLatch();
+        final BooleanLatch acquired2 = new BooleanLatch();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                acquired1.releaseShared(0);
+                await(c, awaitMethod);
+                sync.release();
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                acquired2.releaseShared(0);
+                await(c, awaitMethod);
+                sync.release();
+            }});
+
+        acquired1.acquireShared(0);
+        acquired2.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1, t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        c.signalAll();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        sync.release();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * toString indicates current state
+     */
+    public void testToString() {
+        Mutex sync = new Mutex();
+        assertTrue(sync.toString().contains("State = " + Mutex.UNLOCKED));
+        sync.acquire();
+        assertTrue(sync.toString().contains("State = " + Mutex.LOCKED));
+    }
+
+    /**
+     * A serialized AQS deserializes with current state, but no queued threads
+     */
+    public void testSerialization() {
+        Mutex sync = new Mutex();
+        assertFalse(serialClone(sync).isHeldExclusively());
+        sync.acquire();
+        Thread t = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t);
+        assertTrue(sync.isHeldExclusively());
+
+        Mutex clone = serialClone(sync);
+        assertTrue(clone.isHeldExclusively());
+        assertHasExclusiveQueuedThreads(sync, t);
+        assertHasExclusiveQueuedThreads(clone, NO_THREADS);
+        t.interrupt();
+        awaitTermination(t);
+        sync.release();
+        assertFalse(sync.isHeldExclusively());
+        assertTrue(clone.isHeldExclusively());
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        assertHasExclusiveQueuedThreads(clone, NO_THREADS);
+    }
+
+    /**
+     * tryReleaseShared setting state changes getState
+     */
+    public void testGetStateWithReleaseShared() {
+        final BooleanLatch l = new BooleanLatch();
+        assertFalse(l.isSignalled());
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+    }
+
+    /**
+     * releaseShared has no effect when already signalled
+     */
+    public void testReleaseShared() {
+        final BooleanLatch l = new BooleanLatch();
+        assertFalse(l.isSignalled());
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+    }
+
+    /**
+     * acquireSharedInterruptibly returns after release, but not before
+     */
+    public void testAcquireSharedInterruptibly() {
+        final BooleanLatch l = new BooleanLatch();
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                l.acquireSharedInterruptibly(0);
+                assertTrue(l.isSignalled());
+                l.acquireSharedInterruptibly(0);
+                assertTrue(l.isSignalled());
+            }});
+
+        waitForQueuedThread(l, t);
+        assertFalse(l.isSignalled());
+        assertThreadStaysAlive(t);
+        assertHasSharedQueuedThreads(l, t);
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+        awaitTermination(t);
+    }
+
+    /**
+     * tryAcquireSharedNanos returns after release, but not before
+     */
+    public void testTryAcquireSharedNanos() {
+        final BooleanLatch l = new BooleanLatch();
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                long nanos = MILLISECONDS.toNanos(2 * LONG_DELAY_MS);
+                assertTrue(l.tryAcquireSharedNanos(0, nanos));
+                assertTrue(l.isSignalled());
+                assertTrue(l.tryAcquireSharedNanos(0, nanos));
+                assertTrue(l.isSignalled());
+            }});
+
+        waitForQueuedThread(l, t);
+        assertFalse(l.isSignalled());
+        assertThreadStaysAlive(t);
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+        awaitTermination(t);
+    }
+
+    /**
+     * acquireSharedInterruptibly is interruptible
+     */
+    public void testAcquireSharedInterruptibly_Interruptible() {
+        final BooleanLatch l = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                l.acquireSharedInterruptibly(0);
+            }});
+
+        waitForQueuedThread(l, t);
+        assertFalse(l.isSignalled());
+        t.interrupt();
+        awaitTermination(t);
+        assertFalse(l.isSignalled());
+    }
+
+    /**
+     * tryAcquireSharedNanos is interruptible
+     */
+    public void testTryAcquireSharedNanos_Interruptible() {
+        final BooleanLatch l = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                long nanos = MILLISECONDS.toNanos(2 * LONG_DELAY_MS);
+                l.tryAcquireSharedNanos(0, nanos);
+            }});
+
+        waitForQueuedThread(l, t);
+        assertFalse(l.isSignalled());
+        t.interrupt();
+        awaitTermination(t);
+        assertFalse(l.isSignalled());
+    }
+
+    /**
+     * tryAcquireSharedNanos times out if not released before timeout
+     */
+    public void testTryAcquireSharedNanos_Timeout() {
+        final BooleanLatch l = new BooleanLatch();
+        final BooleanLatch observedQueued = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                for (long millis = timeoutMillis();
+                     !observedQueued.isSignalled();
+                     millis *= 2) {
+                    long nanos = MILLISECONDS.toNanos(millis);
+                    long startTime = System.nanoTime();
+                    assertFalse(l.tryAcquireSharedNanos(0, nanos));
+                    assertTrue(millisElapsedSince(startTime) >= millis);
+                }
+                assertFalse(l.isSignalled());
+            }});
+
+        waitForQueuedThread(l, t);
+        observedQueued.releaseShared(0);
+        assertFalse(l.isSignalled());
+        awaitTermination(t);
+        assertFalse(l.isSignalled());
+    }
+
+    /**
+     * awaitNanos/timed await with 0 wait times out immediately
+     */
+    public void testAwait_Zero() throws InterruptedException {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        sync.acquire();
+        assertTrue(c.awaitNanos(0L) <= 0);
+        assertFalse(c.await(0L, NANOSECONDS));
+        sync.release();
+    }
+
+    /**
+     * awaitNanos/timed await with maximum negative wait times does not underflow
+     */
+    public void testAwait_NegativeInfinity() throws InterruptedException {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        sync.acquire();
+        assertTrue(c.awaitNanos(Long.MIN_VALUE) <= 0);
+        assertFalse(c.await(Long.MIN_VALUE, NANOSECONDS));
+        sync.release();
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/AbstractQueuedSynchronizerTest.java b/jdk/test/java/util/concurrent/tck/AbstractQueuedSynchronizerTest.java
new file mode 100644
index 00000000000..8341a7e296c
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/AbstractQueuedSynchronizerTest.java
@@ -0,0 +1,1283 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.concurrent.locks.AbstractQueuedSynchronizer;
+import java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AbstractQueuedSynchronizerTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AbstractQueuedSynchronizerTest.class);
+    }
+
+    /**
+     * A simple mutex class, adapted from the class javadoc.  Exclusive
+     * acquire tests exercise this as a sample user extension.  Other
+     * methods/features of AbstractQueuedSynchronizer are tested via
+     * other test classes, including those for ReentrantLock,
+     * ReentrantReadWriteLock, and Semaphore.
+     */
+    static class Mutex extends AbstractQueuedSynchronizer {
+        /** An eccentric value for locked synchronizer state. */
+        static final int LOCKED = (1 << 31) | (1 << 15);
+
+        static final int UNLOCKED = 0;
+
+        @Override public boolean isHeldExclusively() {
+            int state = getState();
+            assertTrue(state == UNLOCKED || state == LOCKED);
+            return state == LOCKED;
+        }
+
+        @Override public boolean tryAcquire(int acquires) {
+            assertEquals(LOCKED, acquires);
+            return compareAndSetState(UNLOCKED, LOCKED);
+        }
+
+        @Override public boolean tryRelease(int releases) {
+            if (getState() != LOCKED) throw new IllegalMonitorStateException();
+            assertEquals(LOCKED, releases);
+            setState(UNLOCKED);
+            return true;
+        }
+
+        public boolean tryAcquireNanos(long nanos) throws InterruptedException {
+            return tryAcquireNanos(LOCKED, nanos);
+        }
+
+        public boolean tryAcquire() {
+            return tryAcquire(LOCKED);
+        }
+
+        public boolean tryRelease() {
+            return tryRelease(LOCKED);
+        }
+
+        public void acquire() {
+            acquire(LOCKED);
+        }
+
+        public void acquireInterruptibly() throws InterruptedException {
+            acquireInterruptibly(LOCKED);
+        }
+
+        public void release() {
+            release(LOCKED);
+        }
+
+        public ConditionObject newCondition() {
+            return new ConditionObject();
+        }
+    }
+
+    /**
+     * A simple latch class, to test shared mode.
+     */
+    static class BooleanLatch extends AbstractQueuedSynchronizer {
+        public boolean isSignalled() { return getState() != 0; }
+
+        public int tryAcquireShared(int ignore) {
+            return isSignalled() ? 1 : -1;
+        }
+
+        public boolean tryReleaseShared(int ignore) {
+            setState(1);
+            return true;
+        }
+    }
+
+    /**
+     * A runnable calling acquireInterruptibly that does not expect to
+     * be interrupted.
+     */
+    class InterruptibleSyncRunnable extends CheckedRunnable {
+        final Mutex sync;
+        InterruptibleSyncRunnable(Mutex sync) { this.sync = sync; }
+        public void realRun() throws InterruptedException {
+            sync.acquireInterruptibly();
+        }
+    }
+
+    /**
+     * A runnable calling acquireInterruptibly that expects to be
+     * interrupted.
+     */
+    class InterruptedSyncRunnable extends CheckedInterruptedRunnable {
+        final Mutex sync;
+        InterruptedSyncRunnable(Mutex sync) { this.sync = sync; }
+        public void realRun() throws InterruptedException {
+            sync.acquireInterruptibly();
+        }
+    }
+
+    /** A constant to clarify calls to checking methods below. */
+    static final Thread[] NO_THREADS = new Thread[0];
+
+    /**
+     * Spin-waits until sync.isQueued(t) becomes true.
+     */
+    void waitForQueuedThread(AbstractQueuedSynchronizer sync, Thread t) {
+        long startTime = System.nanoTime();
+        while (!sync.isQueued(t)) {
+            if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                throw new AssertionFailedError("timed out");
+            Thread.yield();
+        }
+        assertTrue(t.isAlive());
+    }
+
+    /**
+     * Checks that sync has exactly the given queued threads.
+     */
+    void assertHasQueuedThreads(AbstractQueuedSynchronizer sync,
+                                Thread... expected) {
+        Collection<Thread> actual = sync.getQueuedThreads();
+        assertEquals(expected.length > 0, sync.hasQueuedThreads());
+        assertEquals(expected.length, sync.getQueueLength());
+        assertEquals(expected.length, actual.size());
+        assertEquals(expected.length == 0, actual.isEmpty());
+        assertEquals(new HashSet<Thread>(actual),
+                     new HashSet<Thread>(Arrays.asList(expected)));
+    }
+
+    /**
+     * Checks that sync has exactly the given (exclusive) queued threads.
+     */
+    void assertHasExclusiveQueuedThreads(AbstractQueuedSynchronizer sync,
+                                         Thread... expected) {
+        assertHasQueuedThreads(sync, expected);
+        assertEquals(new HashSet<Thread>(sync.getExclusiveQueuedThreads()),
+                     new HashSet<Thread>(sync.getQueuedThreads()));
+        assertEquals(0, sync.getSharedQueuedThreads().size());
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+    }
+
+    /**
+     * Checks that sync has exactly the given (shared) queued threads.
+     */
+    void assertHasSharedQueuedThreads(AbstractQueuedSynchronizer sync,
+                                      Thread... expected) {
+        assertHasQueuedThreads(sync, expected);
+        assertEquals(new HashSet<Thread>(sync.getSharedQueuedThreads()),
+                     new HashSet<Thread>(sync.getQueuedThreads()));
+        assertEquals(0, sync.getExclusiveQueuedThreads().size());
+        assertTrue(sync.getExclusiveQueuedThreads().isEmpty());
+    }
+
+    /**
+     * Checks that condition c has exactly the given waiter threads,
+     * after acquiring mutex.
+     */
+    void assertHasWaitersUnlocked(Mutex sync, ConditionObject c,
+                                 Thread... threads) {
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, threads);
+        sync.release();
+    }
+
+    /**
+     * Checks that condition c has exactly the given waiter threads.
+     */
+    void assertHasWaitersLocked(Mutex sync, ConditionObject c,
+                                Thread... threads) {
+        assertEquals(threads.length > 0, sync.hasWaiters(c));
+        assertEquals(threads.length, sync.getWaitQueueLength(c));
+        assertEquals(threads.length == 0, sync.getWaitingThreads(c).isEmpty());
+        assertEquals(threads.length, sync.getWaitingThreads(c).size());
+        assertEquals(new HashSet<Thread>(sync.getWaitingThreads(c)),
+                     new HashSet<Thread>(Arrays.asList(threads)));
+    }
+
+    enum AwaitMethod { await, awaitTimed, awaitNanos, awaitUntil }
+
+    /**
+     * Awaits condition using the specified AwaitMethod.
+     */
+    void await(ConditionObject c, AwaitMethod awaitMethod)
+            throws InterruptedException {
+        long timeoutMillis = 2 * LONG_DELAY_MS;
+        switch (awaitMethod) {
+        case await:
+            c.await();
+            break;
+        case awaitTimed:
+            assertTrue(c.await(timeoutMillis, MILLISECONDS));
+            break;
+        case awaitNanos:
+            long nanosTimeout = MILLISECONDS.toNanos(timeoutMillis);
+            long nanosRemaining = c.awaitNanos(nanosTimeout);
+            assertTrue(nanosRemaining > 0);
+            break;
+        case awaitUntil:
+            assertTrue(c.awaitUntil(delayedDate(timeoutMillis)));
+            break;
+        default:
+            throw new AssertionError();
+        }
+    }
+
+    /**
+     * Checks that awaiting the given condition times out (using the
+     * default timeout duration).
+     */
+    void assertAwaitTimesOut(ConditionObject c, AwaitMethod awaitMethod) {
+        long timeoutMillis = timeoutMillis();
+        long startTime;
+        try {
+            switch (awaitMethod) {
+            case awaitTimed:
+                startTime = System.nanoTime();
+                assertFalse(c.await(timeoutMillis, MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+                break;
+            case awaitNanos:
+                startTime = System.nanoTime();
+                long nanosTimeout = MILLISECONDS.toNanos(timeoutMillis);
+                long nanosRemaining = c.awaitNanos(nanosTimeout);
+                assertTrue(nanosRemaining <= 0);
+                assertTrue(nanosRemaining > -MILLISECONDS.toNanos(LONG_DELAY_MS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+                break;
+            case awaitUntil:
+                // We shouldn't assume that nanoTime and currentTimeMillis
+                // use the same time source, so don't use nanoTime here.
+                java.util.Date delayedDate = delayedDate(timeoutMillis());
+                assertFalse(c.awaitUntil(delayedDate(timeoutMillis)));
+                assertTrue(new java.util.Date().getTime() >= delayedDate.getTime());
+                break;
+            default:
+                throw new UnsupportedOperationException();
+            }
+        } catch (InterruptedException ie) { threadUnexpectedException(ie); }
+    }
+
+    /**
+     * isHeldExclusively is false upon construction
+     */
+    public void testIsHeldExclusively() {
+        Mutex sync = new Mutex();
+        assertFalse(sync.isHeldExclusively());
+    }
+
+    /**
+     * acquiring released sync succeeds
+     */
+    public void testAcquire() {
+        Mutex sync = new Mutex();
+        sync.acquire();
+        assertTrue(sync.isHeldExclusively());
+        sync.release();
+        assertFalse(sync.isHeldExclusively());
+    }
+
+    /**
+     * tryAcquire on a released sync succeeds
+     */
+    public void testTryAcquire() {
+        Mutex sync = new Mutex();
+        assertTrue(sync.tryAcquire());
+        assertTrue(sync.isHeldExclusively());
+        sync.release();
+        assertFalse(sync.isHeldExclusively());
+    }
+
+    /**
+     * hasQueuedThreads reports whether there are waiting threads
+     */
+    public void testHasQueuedThreads() {
+        final Mutex sync = new Mutex();
+        assertFalse(sync.hasQueuedThreads());
+        sync.acquire();
+        Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t1);
+        assertTrue(sync.hasQueuedThreads());
+        Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync));
+        waitForQueuedThread(sync, t2);
+        assertTrue(sync.hasQueuedThreads());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertTrue(sync.hasQueuedThreads());
+        sync.release();
+        awaitTermination(t2);
+        assertFalse(sync.hasQueuedThreads());
+    }
+
+    /**
+     * isQueued(null) throws NullPointerException
+     */
+    public void testIsQueuedNPE() {
+        final Mutex sync = new Mutex();
+        try {
+            sync.isQueued(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * isQueued reports whether a thread is queued
+     */
+    public void testIsQueued() {
+        final Mutex sync = new Mutex();
+        Thread t1 = new Thread(new InterruptedSyncRunnable(sync));
+        Thread t2 = new Thread(new InterruptibleSyncRunnable(sync));
+        assertFalse(sync.isQueued(t1));
+        assertFalse(sync.isQueued(t2));
+        sync.acquire();
+        t1.start();
+        waitForQueuedThread(sync, t1);
+        assertTrue(sync.isQueued(t1));
+        assertFalse(sync.isQueued(t2));
+        t2.start();
+        waitForQueuedThread(sync, t2);
+        assertTrue(sync.isQueued(t1));
+        assertTrue(sync.isQueued(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertFalse(sync.isQueued(t1));
+        assertTrue(sync.isQueued(t2));
+        sync.release();
+        awaitTermination(t2);
+        assertFalse(sync.isQueued(t1));
+        assertFalse(sync.isQueued(t2));
+    }
+
+    /**
+     * getFirstQueuedThread returns first waiting thread or null if none
+     */
+    public void testGetFirstQueuedThread() {
+        final Mutex sync = new Mutex();
+        assertNull(sync.getFirstQueuedThread());
+        sync.acquire();
+        Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t1);
+        assertEquals(t1, sync.getFirstQueuedThread());
+        Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync));
+        waitForQueuedThread(sync, t2);
+        assertEquals(t1, sync.getFirstQueuedThread());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertEquals(t2, sync.getFirstQueuedThread());
+        sync.release();
+        awaitTermination(t2);
+        assertNull(sync.getFirstQueuedThread());
+    }
+
+    /**
+     * hasContended reports false if no thread has ever blocked, else true
+     */
+    public void testHasContended() {
+        final Mutex sync = new Mutex();
+        assertFalse(sync.hasContended());
+        sync.acquire();
+        assertFalse(sync.hasContended());
+        Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t1);
+        assertTrue(sync.hasContended());
+        Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync));
+        waitForQueuedThread(sync, t2);
+        assertTrue(sync.hasContended());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertTrue(sync.hasContended());
+        sync.release();
+        awaitTermination(t2);
+        assertTrue(sync.hasContended());
+    }
+
+    /**
+     * getQueuedThreads returns all waiting threads
+     */
+    public void testGetQueuedThreads() {
+        final Mutex sync = new Mutex();
+        Thread t1 = new Thread(new InterruptedSyncRunnable(sync));
+        Thread t2 = new Thread(new InterruptibleSyncRunnable(sync));
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        sync.acquire();
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        t1.start();
+        waitForQueuedThread(sync, t1);
+        assertHasExclusiveQueuedThreads(sync, t1);
+        assertTrue(sync.getQueuedThreads().contains(t1));
+        assertFalse(sync.getQueuedThreads().contains(t2));
+        t2.start();
+        waitForQueuedThread(sync, t2);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        assertTrue(sync.getQueuedThreads().contains(t1));
+        assertTrue(sync.getQueuedThreads().contains(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertHasExclusiveQueuedThreads(sync, t2);
+        sync.release();
+        awaitTermination(t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+    }
+
+    /**
+     * getExclusiveQueuedThreads returns all exclusive waiting threads
+     */
+    public void testGetExclusiveQueuedThreads() {
+        final Mutex sync = new Mutex();
+        Thread t1 = new Thread(new InterruptedSyncRunnable(sync));
+        Thread t2 = new Thread(new InterruptibleSyncRunnable(sync));
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        sync.acquire();
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        t1.start();
+        waitForQueuedThread(sync, t1);
+        assertHasExclusiveQueuedThreads(sync, t1);
+        assertTrue(sync.getExclusiveQueuedThreads().contains(t1));
+        assertFalse(sync.getExclusiveQueuedThreads().contains(t2));
+        t2.start();
+        waitForQueuedThread(sync, t2);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        assertTrue(sync.getExclusiveQueuedThreads().contains(t1));
+        assertTrue(sync.getExclusiveQueuedThreads().contains(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertHasExclusiveQueuedThreads(sync, t2);
+        sync.release();
+        awaitTermination(t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+    }
+
+    /**
+     * getSharedQueuedThreads does not include exclusively waiting threads
+     */
+    public void testGetSharedQueuedThreads_Exclusive() {
+        final Mutex sync = new Mutex();
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        sync.acquire();
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t1);
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync));
+        waitForQueuedThread(sync, t2);
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        sync.release();
+        awaitTermination(t2);
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+    }
+
+    /**
+     * getSharedQueuedThreads returns all shared waiting threads
+     */
+    public void testGetSharedQueuedThreads_Shared() {
+        final BooleanLatch l = new BooleanLatch();
+        assertHasSharedQueuedThreads(l, NO_THREADS);
+        Thread t1 = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                l.acquireSharedInterruptibly(0);
+            }});
+        waitForQueuedThread(l, t1);
+        assertHasSharedQueuedThreads(l, t1);
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                l.acquireSharedInterruptibly(0);
+            }});
+        waitForQueuedThread(l, t2);
+        assertHasSharedQueuedThreads(l, t1, t2);
+        t1.interrupt();
+        awaitTermination(t1);
+        assertHasSharedQueuedThreads(l, t2);
+        assertTrue(l.releaseShared(0));
+        awaitTermination(t2);
+        assertHasSharedQueuedThreads(l, NO_THREADS);
+    }
+
+    /**
+     * tryAcquireNanos is interruptible
+     */
+    public void testTryAcquireNanos_Interruptible() {
+        final Mutex sync = new Mutex();
+        sync.acquire();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.tryAcquireNanos(MILLISECONDS.toNanos(2 * LONG_DELAY_MS));
+            }});
+
+        waitForQueuedThread(sync, t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * tryAcquire on exclusively held sync fails
+     */
+    public void testTryAcquireWhenSynced() {
+        final Mutex sync = new Mutex();
+        sync.acquire();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertFalse(sync.tryAcquire());
+            }});
+
+        awaitTermination(t);
+        sync.release();
+    }
+
+    /**
+     * tryAcquireNanos on an exclusively held sync times out
+     */
+    public void testAcquireNanos_Timeout() {
+        final Mutex sync = new Mutex();
+        sync.acquire();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                long nanos = MILLISECONDS.toNanos(timeoutMillis());
+                assertFalse(sync.tryAcquireNanos(nanos));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            }});
+
+        awaitTermination(t);
+        sync.release();
+    }
+
+    /**
+     * getState is true when acquired and false when not
+     */
+    public void testGetState() {
+        final Mutex sync = new Mutex();
+        sync.acquire();
+        assertTrue(sync.isHeldExclusively());
+        sync.release();
+        assertFalse(sync.isHeldExclusively());
+
+        final BooleanLatch acquired = new BooleanLatch();
+        final BooleanLatch done = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertTrue(acquired.releaseShared(0));
+                done.acquireShared(0);
+                sync.release();
+            }});
+
+        acquired.acquireShared(0);
+        assertTrue(sync.isHeldExclusively());
+        assertTrue(done.releaseShared(0));
+        awaitTermination(t);
+        assertFalse(sync.isHeldExclusively());
+    }
+
+    /**
+     * acquireInterruptibly succeeds when released, else is interruptible
+     */
+    public void testAcquireInterruptibly() throws InterruptedException {
+        final Mutex sync = new Mutex();
+        final BooleanLatch threadStarted = new BooleanLatch();
+        sync.acquireInterruptibly();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertTrue(threadStarted.releaseShared(0));
+                sync.acquireInterruptibly();
+            }});
+
+        threadStarted.acquireShared(0);
+        waitForQueuedThread(sync, t);
+        t.interrupt();
+        awaitTermination(t);
+        assertTrue(sync.isHeldExclusively());
+    }
+
+    /**
+     * owns is true for a condition created by sync else false
+     */
+    public void testOwns() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final Mutex sync2 = new Mutex();
+        assertTrue(sync.owns(c));
+        assertFalse(sync2.owns(c));
+    }
+
+    /**
+     * Calling await without holding sync throws IllegalMonitorStateException
+     */
+    public void testAwait_IMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        for (AwaitMethod awaitMethod : AwaitMethod.values()) {
+            long startTime = System.nanoTime();
+            try {
+                await(c, awaitMethod);
+                shouldThrow();
+            } catch (IllegalMonitorStateException success) {
+            } catch (InterruptedException e) { threadUnexpectedException(e); }
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * Calling signal without holding sync throws IllegalMonitorStateException
+     */
+    public void testSignal_IMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            c.signal();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * Calling signalAll without holding sync throws IllegalMonitorStateException
+     */
+    public void testSignalAll_IMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            c.signalAll();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * await/awaitNanos/awaitUntil without a signal times out
+     */
+    public void testAwaitTimed_Timeout() { testAwait_Timeout(AwaitMethod.awaitTimed); }
+    public void testAwaitNanos_Timeout() { testAwait_Timeout(AwaitMethod.awaitNanos); }
+    public void testAwaitUntil_Timeout() { testAwait_Timeout(AwaitMethod.awaitUntil); }
+    public void testAwait_Timeout(AwaitMethod awaitMethod) {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        sync.acquire();
+        assertAwaitTimesOut(c, awaitMethod);
+        sync.release();
+    }
+
+    /**
+     * await/awaitNanos/awaitUntil returns when signalled
+     */
+    public void testSignal_await()      { testSignal(AwaitMethod.await); }
+    public void testSignal_awaitTimed() { testSignal(AwaitMethod.awaitTimed); }
+    public void testSignal_awaitNanos() { testSignal(AwaitMethod.awaitNanos); }
+    public void testSignal_awaitUntil() { testSignal(AwaitMethod.awaitUntil); }
+    public void testSignal(final AwaitMethod awaitMethod) {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertTrue(acquired.releaseShared(0));
+                await(c, awaitMethod);
+                sync.release();
+            }});
+
+        acquired.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        c.signal();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t);
+        sync.release();
+        awaitTermination(t);
+    }
+
+    /**
+     * hasWaiters(null) throws NullPointerException
+     */
+    public void testHasWaitersNPE() {
+        final Mutex sync = new Mutex();
+        try {
+            sync.hasWaiters(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * getWaitQueueLength(null) throws NullPointerException
+     */
+    public void testGetWaitQueueLengthNPE() {
+        final Mutex sync = new Mutex();
+        try {
+            sync.getWaitQueueLength(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * getWaitingThreads(null) throws NullPointerException
+     */
+    public void testGetWaitingThreadsNPE() {
+        final Mutex sync = new Mutex();
+        try {
+            sync.getWaitingThreads(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * hasWaiters throws IllegalArgumentException if not owned
+     */
+    public void testHasWaitersIAE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final Mutex sync2 = new Mutex();
+        try {
+            sync2.hasWaiters(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * hasWaiters throws IllegalMonitorStateException if not synced
+     */
+    public void testHasWaitersIMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            sync.hasWaiters(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitQueueLength throws IllegalArgumentException if not owned
+     */
+    public void testGetWaitQueueLengthIAE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final Mutex sync2 = new Mutex();
+        try {
+            sync2.getWaitQueueLength(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitQueueLength throws IllegalMonitorStateException if not synced
+     */
+    public void testGetWaitQueueLengthIMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            sync.getWaitQueueLength(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitingThreads throws IllegalArgumentException if not owned
+     */
+    public void testGetWaitingThreadsIAE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final Mutex sync2 = new Mutex();
+        try {
+            sync2.getWaitingThreads(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitingThreads throws IllegalMonitorStateException if not synced
+     */
+    public void testGetWaitingThreadsIMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            sync.getWaitingThreads(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * hasWaiters returns true when a thread is waiting, else false
+     */
+    public void testHasWaiters() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, NO_THREADS);
+                assertFalse(sync.hasWaiters(c));
+                assertTrue(acquired.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+
+        acquired.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        assertTrue(sync.hasWaiters(c));
+        c.signal();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t);
+        assertFalse(sync.hasWaiters(c));
+        sync.release();
+
+        awaitTermination(t);
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitQueueLength returns number of waiting threads
+     */
+    public void testGetWaitQueueLength() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired1 = new BooleanLatch();
+        final BooleanLatch acquired2 = new BooleanLatch();
+        final Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, NO_THREADS);
+                assertEquals(0, sync.getWaitQueueLength(c));
+                assertTrue(acquired1.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+        acquired1.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1);
+        assertEquals(1, sync.getWaitQueueLength(c));
+        sync.release();
+
+        final Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, t1);
+                assertEquals(1, sync.getWaitQueueLength(c));
+                assertTrue(acquired2.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+        acquired2.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1, t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        assertEquals(2, sync.getWaitQueueLength(c));
+        c.signalAll();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        assertEquals(0, sync.getWaitQueueLength(c));
+        sync.release();
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitingThreads returns only and all waiting threads
+     */
+    public void testGetWaitingThreads() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired1 = new BooleanLatch();
+        final BooleanLatch acquired2 = new BooleanLatch();
+        final Thread t1 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, NO_THREADS);
+                assertTrue(sync.getWaitingThreads(c).isEmpty());
+                assertTrue(acquired1.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+
+        final Thread t2 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, t1);
+                assertTrue(sync.getWaitingThreads(c).contains(t1));
+                assertFalse(sync.getWaitingThreads(c).isEmpty());
+                assertEquals(1, sync.getWaitingThreads(c).size());
+                assertTrue(acquired2.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertFalse(sync.getWaitingThreads(c).contains(t1));
+        assertFalse(sync.getWaitingThreads(c).contains(t2));
+        assertTrue(sync.getWaitingThreads(c).isEmpty());
+        assertEquals(0, sync.getWaitingThreads(c).size());
+        sync.release();
+
+        t1.start();
+        acquired1.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1);
+        assertTrue(sync.getWaitingThreads(c).contains(t1));
+        assertFalse(sync.getWaitingThreads(c).contains(t2));
+        assertFalse(sync.getWaitingThreads(c).isEmpty());
+        assertEquals(1, sync.getWaitingThreads(c).size());
+        sync.release();
+
+        t2.start();
+        acquired2.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1, t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        assertTrue(sync.getWaitingThreads(c).contains(t1));
+        assertTrue(sync.getWaitingThreads(c).contains(t2));
+        assertFalse(sync.getWaitingThreads(c).isEmpty());
+        assertEquals(2, sync.getWaitingThreads(c).size());
+        c.signalAll();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        assertFalse(sync.getWaitingThreads(c).contains(t1));
+        assertFalse(sync.getWaitingThreads(c).contains(t2));
+        assertTrue(sync.getWaitingThreads(c).isEmpty());
+        assertEquals(0, sync.getWaitingThreads(c).size());
+        sync.release();
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * awaitUninterruptibly is uninterruptible
+     */
+    public void testAwaitUninterruptibly() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch pleaseInterrupt = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                sync.acquire();
+                assertTrue(pleaseInterrupt.releaseShared(0));
+                c.awaitUninterruptibly();
+                assertTrue(Thread.interrupted());
+                assertHasWaitersLocked(sync, c, NO_THREADS);
+                sync.release();
+            }});
+
+        pleaseInterrupt.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t);
+        sync.release();
+        t.interrupt();
+        assertHasWaitersUnlocked(sync, c, t);
+        assertThreadStaysAlive(t);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        c.signal();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t);
+        sync.release();
+        awaitTermination(t);
+    }
+
+    /**
+     * await/awaitNanos/awaitUntil is interruptible
+     */
+    public void testInterruptible_await()      { testInterruptible(AwaitMethod.await); }
+    public void testInterruptible_awaitTimed() { testInterruptible(AwaitMethod.awaitTimed); }
+    public void testInterruptible_awaitNanos() { testInterruptible(AwaitMethod.awaitNanos); }
+    public void testInterruptible_awaitUntil() { testInterruptible(AwaitMethod.awaitUntil); }
+    public void testInterruptible(final AwaitMethod awaitMethod) {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch pleaseInterrupt = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertTrue(pleaseInterrupt.releaseShared(0));
+                await(c, awaitMethod);
+            }});
+
+        pleaseInterrupt.acquireShared(0);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * signalAll wakes up all threads
+     */
+    public void testSignalAll_await()      { testSignalAll(AwaitMethod.await); }
+    public void testSignalAll_awaitTimed() { testSignalAll(AwaitMethod.awaitTimed); }
+    public void testSignalAll_awaitNanos() { testSignalAll(AwaitMethod.awaitNanos); }
+    public void testSignalAll_awaitUntil() { testSignalAll(AwaitMethod.awaitUntil); }
+    public void testSignalAll(final AwaitMethod awaitMethod) {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired1 = new BooleanLatch();
+        final BooleanLatch acquired2 = new BooleanLatch();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                acquired1.releaseShared(0);
+                await(c, awaitMethod);
+                sync.release();
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                acquired2.releaseShared(0);
+                await(c, awaitMethod);
+                sync.release();
+            }});
+
+        acquired1.acquireShared(0);
+        acquired2.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1, t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        c.signalAll();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        sync.release();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * toString indicates current state
+     */
+    public void testToString() {
+        Mutex sync = new Mutex();
+        assertTrue(sync.toString().contains("State = " + Mutex.UNLOCKED));
+        sync.acquire();
+        assertTrue(sync.toString().contains("State = " + Mutex.LOCKED));
+    }
+
+    /**
+     * A serialized AQS deserializes with current state, but no queued threads
+     */
+    public void testSerialization() {
+        Mutex sync = new Mutex();
+        assertFalse(serialClone(sync).isHeldExclusively());
+        sync.acquire();
+        Thread t = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t);
+        assertTrue(sync.isHeldExclusively());
+
+        Mutex clone = serialClone(sync);
+        assertTrue(clone.isHeldExclusively());
+        assertHasExclusiveQueuedThreads(sync, t);
+        assertHasExclusiveQueuedThreads(clone, NO_THREADS);
+        t.interrupt();
+        awaitTermination(t);
+        sync.release();
+        assertFalse(sync.isHeldExclusively());
+        assertTrue(clone.isHeldExclusively());
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        assertHasExclusiveQueuedThreads(clone, NO_THREADS);
+    }
+
+    /**
+     * tryReleaseShared setting state changes getState
+     */
+    public void testGetStateWithReleaseShared() {
+        final BooleanLatch l = new BooleanLatch();
+        assertFalse(l.isSignalled());
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+    }
+
+    /**
+     * releaseShared has no effect when already signalled
+     */
+    public void testReleaseShared() {
+        final BooleanLatch l = new BooleanLatch();
+        assertFalse(l.isSignalled());
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+    }
+
+    /**
+     * acquireSharedInterruptibly returns after release, but not before
+     */
+    public void testAcquireSharedInterruptibly() {
+        final BooleanLatch l = new BooleanLatch();
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                l.acquireSharedInterruptibly(0);
+                assertTrue(l.isSignalled());
+                l.acquireSharedInterruptibly(0);
+                assertTrue(l.isSignalled());
+            }});
+
+        waitForQueuedThread(l, t);
+        assertFalse(l.isSignalled());
+        assertThreadStaysAlive(t);
+        assertHasSharedQueuedThreads(l, t);
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+        awaitTermination(t);
+    }
+
+    /**
+     * tryAcquireSharedNanos returns after release, but not before
+     */
+    public void testTryAcquireSharedNanos() {
+        final BooleanLatch l = new BooleanLatch();
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                long nanos = MILLISECONDS.toNanos(2 * LONG_DELAY_MS);
+                assertTrue(l.tryAcquireSharedNanos(0, nanos));
+                assertTrue(l.isSignalled());
+                assertTrue(l.tryAcquireSharedNanos(0, nanos));
+                assertTrue(l.isSignalled());
+            }});
+
+        waitForQueuedThread(l, t);
+        assertFalse(l.isSignalled());
+        assertThreadStaysAlive(t);
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+        awaitTermination(t);
+    }
+
+    /**
+     * acquireSharedInterruptibly is interruptible
+     */
+    public void testAcquireSharedInterruptibly_Interruptible() {
+        final BooleanLatch l = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                l.acquireSharedInterruptibly(0);
+            }});
+
+        waitForQueuedThread(l, t);
+        assertFalse(l.isSignalled());
+        t.interrupt();
+        awaitTermination(t);
+        assertFalse(l.isSignalled());
+    }
+
+    /**
+     * tryAcquireSharedNanos is interruptible
+     */
+    public void testTryAcquireSharedNanos_Interruptible() {
+        final BooleanLatch l = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                long nanos = MILLISECONDS.toNanos(2 * LONG_DELAY_MS);
+                l.tryAcquireSharedNanos(0, nanos);
+            }});
+
+        waitForQueuedThread(l, t);
+        assertFalse(l.isSignalled());
+        t.interrupt();
+        awaitTermination(t);
+        assertFalse(l.isSignalled());
+    }
+
+    /**
+     * tryAcquireSharedNanos times out if not released before timeout
+     */
+    public void testTryAcquireSharedNanos_Timeout() {
+        final BooleanLatch l = new BooleanLatch();
+        final BooleanLatch observedQueued = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                for (long millis = timeoutMillis();
+                     !observedQueued.isSignalled();
+                     millis *= 2) {
+                    long nanos = MILLISECONDS.toNanos(millis);
+                    long startTime = System.nanoTime();
+                    assertFalse(l.tryAcquireSharedNanos(0, nanos));
+                    assertTrue(millisElapsedSince(startTime) >= millis);
+                }
+                assertFalse(l.isSignalled());
+            }});
+
+        waitForQueuedThread(l, t);
+        observedQueued.releaseShared(0);
+        assertFalse(l.isSignalled());
+        awaitTermination(t);
+        assertFalse(l.isSignalled());
+    }
+
+    /**
+     * awaitNanos/timed await with 0 wait times out immediately
+     */
+    public void testAwait_Zero() throws InterruptedException {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        sync.acquire();
+        assertTrue(c.awaitNanos(0L) <= 0);
+        assertFalse(c.await(0L, NANOSECONDS));
+        sync.release();
+    }
+
+    /**
+     * awaitNanos/timed await with maximum negative wait times does not underflow
+     */
+    public void testAwait_NegativeInfinity() throws InterruptedException {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        sync.acquire();
+        assertTrue(c.awaitNanos(Long.MIN_VALUE) <= 0);
+        assertFalse(c.await(Long.MIN_VALUE, NANOSECONDS));
+        sync.release();
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/ArrayBlockingQueueTest.java b/jdk/test/java/util/concurrent/tck/ArrayBlockingQueueTest.java
new file mode 100644
index 00000000000..e7fa0316197
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/ArrayBlockingQueueTest.java
@@ -0,0 +1,955 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+
+import junit.framework.Test;
+
+public class ArrayBlockingQueueTest extends JSR166TestCase {
+
+    public static class Fair extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new ArrayBlockingQueue(SIZE, true);
+        }
+    }
+
+    public static class NonFair extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new ArrayBlockingQueue(SIZE, false);
+        }
+    }
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return newTestSuite(ArrayBlockingQueueTest.class,
+                            new Fair().testSuite(),
+                            new NonFair().testSuite());
+    }
+
+    /**
+     * Returns a new queue of given size containing consecutive
+     * Integers 0 ... n.
+     */
+    private ArrayBlockingQueue<Integer> populatedQueue(int n) {
+        ArrayBlockingQueue<Integer> q = new ArrayBlockingQueue<Integer>(n);
+        assertTrue(q.isEmpty());
+        for (int i = 0; i < n; i++)
+            assertTrue(q.offer(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(0, q.remainingCapacity());
+        assertEquals(n, q.size());
+        return q;
+    }
+
+    /**
+     * A new queue has the indicated capacity
+     */
+    public void testConstructor1() {
+        assertEquals(SIZE, new ArrayBlockingQueue(SIZE).remainingCapacity());
+    }
+
+    /**
+     * Constructor throws IAE if capacity argument nonpositive
+     */
+    public void testConstructor2() {
+        try {
+            new ArrayBlockingQueue(0);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            new ArrayBlockingQueue(1, true, null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NPE
+     */
+    public void testConstructor4() {
+        Collection<Integer> elements = Arrays.asList(new Integer[SIZE]);
+        try {
+            new ArrayBlockingQueue(SIZE, false, elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws NPE
+     */
+    public void testConstructor5() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = i;
+        Collection<Integer> elements = Arrays.asList(ints);
+        try {
+            new ArrayBlockingQueue(SIZE, false, elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from too large collection throws IAE
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = i;
+        Collection<Integer> elements = Arrays.asList(ints);
+        try {
+            new ArrayBlockingQueue(SIZE - 1, false, elements);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Queue contains all elements of collection used to initialize
+     */
+    public void testConstructor7() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = i;
+        Collection<Integer> elements = Arrays.asList(ints);
+        ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE, true, elements);
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * Queue transitions from empty to full when elements added
+     */
+    public void testEmptyFull() {
+        ArrayBlockingQueue q = new ArrayBlockingQueue(2);
+        assertTrue(q.isEmpty());
+        assertEquals(2, q.remainingCapacity());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        q.add(two);
+        assertFalse(q.isEmpty());
+        assertEquals(0, q.remainingCapacity());
+        assertFalse(q.offer(three));
+    }
+
+    /**
+     * remainingCapacity decreases on add, increases on remove
+     */
+    public void testRemainingCapacity() {
+        BlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remainingCapacity());
+            assertEquals(SIZE, q.size() + q.remainingCapacity());
+            assertEquals(i, q.remove());
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i, q.remainingCapacity());
+            assertEquals(SIZE, q.size() + q.remainingCapacity());
+            assertTrue(q.add(i));
+        }
+    }
+
+    /**
+     * Offer succeeds if not full; fails if full
+     */
+    public void testOffer() {
+        ArrayBlockingQueue q = new ArrayBlockingQueue(1);
+        assertTrue(q.offer(zero));
+        assertFalse(q.offer(one));
+    }
+
+    /**
+     * add succeeds if not full; throws ISE if full
+     */
+    public void testAdd() {
+        ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.add(new Integer(i)));
+        }
+        assertEquals(0, q.remainingCapacity());
+        try {
+            q.add(new Integer(SIZE));
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * addAll(this) throws IAE
+     */
+    public void testAddAllSelf() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        try {
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll throws ISE if not enough room
+     */
+    public void testAddAll4() {
+        ArrayBlockingQueue q = new ArrayBlockingQueue(1);
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * Queue contains all elements, in traversal order, of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE);
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * all elements successfully put are contained
+     */
+    public void testPut() throws InterruptedException {
+        ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            Integer x = new Integer(i);
+            q.put(x);
+            assertTrue(q.contains(x));
+        }
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * put blocks interruptibly if full
+     */
+    public void testBlockingPut() throws InterruptedException {
+        final ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i)
+                    q.put(i);
+                assertEquals(SIZE, q.size());
+                assertEquals(0, q.remainingCapacity());
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(SIZE, q.size());
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * put blocks interruptibly waiting for take when full
+     */
+    public void testPutWithTake() throws InterruptedException {
+        final int capacity = 2;
+        final ArrayBlockingQueue q = new ArrayBlockingQueue(capacity);
+        final CountDownLatch pleaseTake = new CountDownLatch(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < capacity; i++)
+                    q.put(i);
+                pleaseTake.countDown();
+                q.put(86);
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseTake);
+        assertEquals(0, q.remainingCapacity());
+        assertEquals(0, q.take());
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * timed offer times out if full and elements not taken
+     */
+    public void testTimedOffer() throws InterruptedException {
+        final ArrayBlockingQueue q = new ArrayBlockingQueue(2);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(new Object());
+                q.put(new Object());
+                long startTime = System.nanoTime();
+                assertFalse(q.offer(new Object(), timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                pleaseInterrupt.countDown();
+                try {
+                    q.offer(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * take retrieves elements in FIFO order
+     */
+    public void testTake() throws InterruptedException {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.take());
+        }
+    }
+
+    /**
+     * Take removes existing elements until empty, then blocks interruptibly
+     */
+    public void testBlockingTake() throws InterruptedException {
+        final ArrayBlockingQueue q = populatedQueue(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(i, q.take());
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll());
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * timed poll with zero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll0() throws InterruptedException {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll(0, MILLISECONDS));
+        }
+        assertNull(q.poll(0, MILLISECONDS));
+        checkEmpty(q);
+    }
+
+    /**
+     * timed poll with nonzero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll() throws InterruptedException {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            long startTime = System.nanoTime();
+            assertEquals(i, q.poll(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+        long startTime = System.nanoTime();
+        assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        checkEmpty(q);
+    }
+
+    /**
+     * Interrupted timed poll throws InterruptedException instead of
+     * returning timeout status
+     */
+    public void testInterruptedTimedPoll() throws InterruptedException {
+        final BlockingQueue<Integer> q = populatedQueue(SIZE);
+        final CountDownLatch aboutToWait = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
+                }
+                aboutToWait.countDown();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {
+                    assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+                }
+            }});
+
+        await(aboutToWait);
+        waitForThreadToEnterWaitState(t, LONG_DELAY_MS);
+        t.interrupt();
+        awaitTermination(t);
+        checkEmpty(q);
+    }
+
+    /**
+     * peek returns next element, or null if empty
+     */
+    public void testPeek() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peek());
+            assertEquals(i, q.poll());
+            assertTrue(q.peek() == null ||
+                       !q.peek().equals(i));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * element returns next element, or throws NSEE if empty
+     */
+    public void testElement() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.element());
+            assertEquals(i, q.poll());
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            assertEquals(i, q.poll());
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        assertEquals(SIZE, q.remainingCapacity());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(one));
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        ArrayBlockingQueue p = new ArrayBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        ArrayBlockingQueue p = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            ArrayBlockingQueue q = populatedQueue(SIZE);
+            ArrayBlockingQueue p = populatedQueue(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer x = (Integer)(p.remove());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    void checkToArray(ArrayBlockingQueue q) {
+        int size = q.size();
+        Object[] o = q.toArray();
+        assertEquals(size, o.length);
+        Iterator it = q.iterator();
+        for (int i = 0; i < size; i++) {
+            Integer x = (Integer) it.next();
+            assertEquals((Integer)o[0] + i, (int) x);
+            assertSame(o[i], x);
+        }
+    }
+
+    /**
+     * toArray() contains all elements in FIFO order
+     */
+    public void testToArray() {
+        ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            checkToArray(q);
+            q.add(i);
+        }
+        // Provoke wraparound
+        for (int i = 0; i < SIZE; i++) {
+            checkToArray(q);
+            assertEquals(i, q.poll());
+            checkToArray(q);
+            q.add(SIZE + i);
+        }
+        for (int i = 0; i < SIZE; i++) {
+            checkToArray(q);
+            assertEquals(SIZE + i, q.poll());
+        }
+    }
+
+    void checkToArray2(ArrayBlockingQueue q) {
+        int size = q.size();
+        Integer[] a1 = (size == 0) ? null : new Integer[size - 1];
+        Integer[] a2 = new Integer[size];
+        Integer[] a3 = new Integer[size + 2];
+        if (size > 0) Arrays.fill(a1, 42);
+        Arrays.fill(a2, 42);
+        Arrays.fill(a3, 42);
+        Integer[] b1 = (size == 0) ? null : (Integer[]) q.toArray(a1);
+        Integer[] b2 = (Integer[]) q.toArray(a2);
+        Integer[] b3 = (Integer[]) q.toArray(a3);
+        assertSame(a2, b2);
+        assertSame(a3, b3);
+        Iterator it = q.iterator();
+        for (int i = 0; i < size; i++) {
+            Integer x = (Integer) it.next();
+            assertSame(b1[i], x);
+            assertEquals(b1[0] + i, (int) x);
+            assertSame(b2[i], x);
+            assertSame(b3[i], x);
+        }
+        assertNull(a3[size]);
+        assertEquals(42, (int) a3[size + 1]);
+        if (size > 0) {
+            assertNotSame(a1, b1);
+            assertEquals(size, b1.length);
+            for (int i = 0; i < a1.length; i++) {
+                assertEquals(42, (int) a1[i]);
+            }
+        }
+    }
+
+    /**
+     * toArray(a) contains all elements in FIFO order
+     */
+    public void testToArray2() {
+        ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            checkToArray2(q);
+            q.add(i);
+        }
+        // Provoke wraparound
+        for (int i = 0; i < SIZE; i++) {
+            checkToArray2(q);
+            assertEquals(i, q.poll());
+            checkToArray2(q);
+            q.add(SIZE + i);
+        }
+        for (int i = 0; i < SIZE; i++) {
+            checkToArray2(q);
+            assertEquals(SIZE + i, q.poll());
+        }
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray1_BadArg() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        try {
+            q.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() throws InterruptedException {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        Iterator it = q.iterator();
+        int i;
+        for (i = 0; it.hasNext(); i++)
+            assertTrue(q.contains(it.next()));
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+
+        it = q.iterator();
+        for (i = 0; it.hasNext(); i++)
+            assertEquals(it.next(), q.take());
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty collection has no elements
+     */
+    public void testEmptyIterator() {
+        assertIteratorExhausted(new ArrayBlockingQueue(SIZE).iterator());
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final ArrayBlockingQueue q = new ArrayBlockingQueue(3);
+        q.add(two);
+        q.add(one);
+        q.add(three);
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertSame(it.next(), one);
+        assertSame(it.next(), three);
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * iterator ordering is FIFO
+     */
+    public void testIteratorOrdering() {
+        final ArrayBlockingQueue q = new ArrayBlockingQueue(3);
+        q.add(one);
+        q.add(two);
+        q.add(three);
+
+        assertEquals("queue should be full", 0, q.remainingCapacity());
+
+        int k = 0;
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            assertEquals(++k, it.next());
+        }
+        assertEquals(3, k);
+    }
+
+    /**
+     * Modifications do not cause iterators to fail
+     */
+    public void testWeaklyConsistentIteration() {
+        final ArrayBlockingQueue q = new ArrayBlockingQueue(3);
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            q.remove();
+            it.next();
+        }
+        assertEquals(0, q.size());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * offer transfers elements across Executor tasks
+     */
+    public void testOfferInExecutor() {
+        final ArrayBlockingQueue q = new ArrayBlockingQueue(2);
+        q.add(one);
+        q.add(two);
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        final ExecutorService executor = Executors.newFixedThreadPool(2);
+        try (PoolCleaner cleaner = cleaner(executor)) {
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(q.offer(three));
+                    threadsStarted.await();
+                    assertTrue(q.offer(three, LONG_DELAY_MS, MILLISECONDS));
+                    assertEquals(0, q.remainingCapacity());
+                }});
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadsStarted.await();
+                    assertEquals(0, q.remainingCapacity());
+                    assertSame(one, q.take());
+                }});
+        }
+    }
+
+    /**
+     * timed poll retrieves elements across Executor threads
+     */
+    public void testPollInExecutor() {
+        final ArrayBlockingQueue q = new ArrayBlockingQueue(2);
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        final ExecutorService executor = Executors.newFixedThreadPool(2);
+        try (PoolCleaner cleaner = cleaner(executor)) {
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertNull(q.poll());
+                    threadsStarted.await();
+                    assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
+                    checkEmpty(q);
+                }});
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadsStarted.await();
+                    q.put(one);
+                }});
+        }
+    }
+
+    /**
+     * A deserialized serialized queue has same elements in same order
+     */
+    public void testSerialization() throws Exception {
+        Queue x = populatedQueue(SIZE);
+        Queue y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertTrue(Arrays.equals(x.toArray(), y.toArray()));
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(), y.remove());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * drainTo(c) empties queue into another collection c
+     */
+    public void testDrainTo() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(SIZE, l.size());
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(l.get(i), new Integer(i));
+        q.add(zero);
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(zero));
+        assertTrue(q.contains(one));
+        l.clear();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(2, l.size());
+        for (int i = 0; i < 2; ++i)
+            assertEquals(l.get(i), new Integer(i));
+    }
+
+    /**
+     * drainTo empties full queue, unblocking a waiting put.
+     */
+    public void testDrainToWithActivePut() throws InterruptedException {
+        final ArrayBlockingQueue q = populatedQueue(SIZE);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(new Integer(SIZE + 1));
+            }});
+
+        t.start();
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertTrue(l.size() >= SIZE);
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(l.get(i), new Integer(i));
+        t.join();
+        assertTrue(q.size() + l.size() >= SIZE);
+    }
+
+    /**
+     * drainTo(c, n) empties first min(n, size) elements of queue into c
+     */
+    public void testDrainToN() {
+        ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE * 2);
+        for (int i = 0; i < SIZE + 2; ++i) {
+            for (int j = 0; j < SIZE; j++)
+                assertTrue(q.offer(new Integer(j)));
+            ArrayList l = new ArrayList();
+            q.drainTo(l, i);
+            int k = (i < SIZE) ? i : SIZE;
+            assertEquals(k, l.size());
+            assertEquals(SIZE - k, q.size());
+            for (int j = 0; j < k; ++j)
+                assertEquals(l.get(j), new Integer(j));
+            do {} while (q.poll() != null);
+        }
+    }
+
+    /**
+     * remove(null), contains(null) always return false
+     */
+    public void testNeverContainsNull() {
+        Collection<?>[] qs = {
+            new ArrayBlockingQueue<Object>(10),
+            populatedQueue(2),
+        };
+
+        for (Collection<?> q : qs) {
+            assertFalse(q.contains(null));
+            assertFalse(q.remove(null));
+        }
+    }
+}
diff --git a/jdk/test/java/util/concurrent/tck/ArrayDequeTest.java b/jdk/test/java/util/concurrent/tck/ArrayDequeTest.java
new file mode 100644
index 00000000000..4241f59ef3d
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/ArrayDequeTest.java
@@ -0,0 +1,945 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import java.util.ArrayDeque;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.Random;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ArrayDequeTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(ArrayDequeTest.class);
+    }
+
+    /**
+     * Returns a new deque of given size containing consecutive
+     * Integers 0 ... n.
+     */
+    private ArrayDeque<Integer> populatedDeque(int n) {
+        ArrayDeque<Integer> q = new ArrayDeque<Integer>();
+        assertTrue(q.isEmpty());
+        for (int i = 0; i < n; ++i)
+            assertTrue(q.offerLast(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(n, q.size());
+        return q;
+    }
+
+    /**
+     * new deque is empty
+     */
+    public void testConstructor1() {
+        assertEquals(0, new ArrayDeque().size());
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            new ArrayDeque((Collection)null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NPE
+     */
+    public void testConstructor4() {
+        try {
+            new ArrayDeque(Arrays.asList(new Integer[SIZE]));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws NPE
+     */
+    public void testConstructor5() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            new ArrayDeque(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Deque contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        ArrayDeque q = new ArrayDeque(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.pollFirst());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        ArrayDeque q = new ArrayDeque();
+        assertTrue(q.isEmpty());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.add(new Integer(2));
+        q.removeFirst();
+        q.removeFirst();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testSize() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i, q.size());
+            q.removeFirst();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * push(null) throws NPE
+     */
+    public void testPushNull() {
+        ArrayDeque q = new ArrayDeque(1);
+        try {
+            q.push(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * peekFirst() returns element inserted with push
+     */
+    public void testPush() {
+        ArrayDeque q = populatedDeque(3);
+        q.pollLast();
+        q.push(four);
+        assertSame(four, q.peekFirst());
+    }
+
+    /**
+     * pop() removes next element, or throws NSEE if empty
+     */
+    public void testPop() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pop());
+        }
+        try {
+            q.pop();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * offer(null) throws NPE
+     */
+    public void testOfferNull() {
+        ArrayDeque q = new ArrayDeque();
+        try {
+            q.offer(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * offerFirst(null) throws NPE
+     */
+    public void testOfferFirstNull() {
+        ArrayDeque q = new ArrayDeque();
+        try {
+            q.offerFirst(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * offerLast(null) throws NPE
+     */
+    public void testOfferLastNull() {
+        ArrayDeque q = new ArrayDeque();
+        try {
+            q.offerLast(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * offer(x) succeeds
+     */
+    public void testOffer() {
+        ArrayDeque q = new ArrayDeque();
+        assertTrue(q.offer(zero));
+        assertTrue(q.offer(one));
+        assertSame(zero, q.peekFirst());
+        assertSame(one, q.peekLast());
+    }
+
+    /**
+     * offerFirst(x) succeeds
+     */
+    public void testOfferFirst() {
+        ArrayDeque q = new ArrayDeque();
+        assertTrue(q.offerFirst(zero));
+        assertTrue(q.offerFirst(one));
+        assertSame(one, q.peekFirst());
+        assertSame(zero, q.peekLast());
+    }
+
+    /**
+     * offerLast(x) succeeds
+     */
+    public void testOfferLast() {
+        ArrayDeque q = new ArrayDeque();
+        assertTrue(q.offerLast(zero));
+        assertTrue(q.offerLast(one));
+        assertSame(zero, q.peekFirst());
+        assertSame(one, q.peekLast());
+    }
+
+    /**
+     * add(null) throws NPE
+     */
+    public void testAddNull() {
+        ArrayDeque q = new ArrayDeque();
+        try {
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addFirst(null) throws NPE
+     */
+    public void testAddFirstNull() {
+        ArrayDeque q = new ArrayDeque();
+        try {
+            q.addFirst(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addLast(null) throws NPE
+     */
+    public void testAddLastNull() {
+        ArrayDeque q = new ArrayDeque();
+        try {
+            q.addLast(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * add(x) succeeds
+     */
+    public void testAdd() {
+        ArrayDeque q = new ArrayDeque();
+        assertTrue(q.add(zero));
+        assertTrue(q.add(one));
+        assertSame(zero, q.peekFirst());
+        assertSame(one, q.peekLast());
+    }
+
+    /**
+     * addFirst(x) succeeds
+     */
+    public void testAddFirst() {
+        ArrayDeque q = new ArrayDeque();
+        q.addFirst(zero);
+        q.addFirst(one);
+        assertSame(one, q.peekFirst());
+        assertSame(zero, q.peekLast());
+    }
+
+    /**
+     * addLast(x) succeeds
+     */
+    public void testAddLast() {
+        ArrayDeque q = new ArrayDeque();
+        q.addLast(zero);
+        q.addLast(one);
+        assertSame(zero, q.peekFirst());
+        assertSame(one, q.peekLast());
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        ArrayDeque q = new ArrayDeque();
+        try {
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testAddAll2() {
+        ArrayDeque q = new ArrayDeque();
+        try {
+            q.addAll(Arrays.asList(new Integer[SIZE]));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        ArrayDeque q = new ArrayDeque();
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Deque contains all elements, in traversal order, of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        ArrayDeque q = new ArrayDeque();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.pollFirst());
+    }
+
+    /**
+     * pollFirst() succeeds unless empty
+     */
+    public void testPollFirst() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * pollLast() succeeds unless empty
+     */
+    public void testPollLast() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.pollLast());
+        }
+        assertNull(q.pollLast());
+    }
+
+    /**
+     * poll() succeeds unless empty
+     */
+    public void testPoll() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll());
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * remove() removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testRemoveElement() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertTrue(q.contains(i - 1));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertFalse(q.remove(i + 1));
+            assertFalse(q.contains(i + 1));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * peekFirst() returns next element, or null if empty
+     */
+    public void testPeekFirst() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peekFirst());
+            assertEquals(i, q.pollFirst());
+            assertTrue(q.peekFirst() == null ||
+                       !q.peekFirst().equals(i));
+        }
+        assertNull(q.peekFirst());
+    }
+
+    /**
+     * peek() returns next element, or null if empty
+     */
+    public void testPeek() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peek());
+            assertEquals(i, q.poll());
+            assertTrue(q.peek() == null ||
+                       !q.peek().equals(i));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * peekLast() returns next element, or null if empty
+     */
+    public void testPeekLast() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.peekLast());
+            assertEquals(i, q.pollLast());
+            assertTrue(q.peekLast() == null ||
+                       !q.peekLast().equals(i));
+        }
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * element() returns first element, or throws NSEE if empty
+     */
+    public void testElement() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.element());
+            assertEquals(i, q.poll());
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * getFirst() returns first element, or throws NSEE if empty
+     */
+    public void testFirstElement() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.getFirst());
+            assertEquals(i, q.pollFirst());
+        }
+        try {
+            q.getFirst();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * getLast() returns last element, or throws NSEE if empty
+     */
+    public void testLastElement() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.getLast());
+            assertEquals(i, q.pollLast());
+        }
+        try {
+            q.getLast();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * removeFirst() removes first element, or throws NSEE if empty
+     */
+    public void testRemoveFirst() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.removeFirst());
+        }
+        try {
+            q.removeFirst();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekFirst());
+    }
+
+    /**
+     * removeLast() removes last element, or throws NSEE if empty
+     */
+    public void testRemoveLast() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.removeLast());
+        }
+        try {
+            q.removeLast();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * removeFirstOccurrence(x) removes x and returns true if present
+     */
+    public void testRemoveFirstOccurrence() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.removeFirstOccurrence(new Integer(i)));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.removeFirstOccurrence(new Integer(i)));
+            assertFalse(q.removeFirstOccurrence(new Integer(i + 1)));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * removeLastOccurrence(x) removes x and returns true if present
+     */
+    public void testRemoveLastOccurrence() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.removeLastOccurrence(new Integer(i)));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.removeLastOccurrence(new Integer(i)));
+            assertFalse(q.removeLastOccurrence(new Integer(i + 1)));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            assertEquals(i, q.pollFirst());
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        ArrayDeque q = populatedDeque(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        assertTrue(q.add(new Integer(1)));
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        ArrayDeque q = populatedDeque(SIZE);
+        ArrayDeque p = new ArrayDeque();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            assertTrue(p.add(new Integer(i)));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        ArrayDeque q = populatedDeque(SIZE);
+        ArrayDeque p = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            assertEquals(changed, (i > 0));
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.removeFirst();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            ArrayDeque q = populatedDeque(SIZE);
+            ArrayDeque p = populatedDeque(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                assertFalse(q.contains(p.removeFirst()));
+            }
+        }
+    }
+
+    void checkToArray(ArrayDeque q) {
+        int size = q.size();
+        Object[] o = q.toArray();
+        assertEquals(size, o.length);
+        Iterator it = q.iterator();
+        for (int i = 0; i < size; i++) {
+            Integer x = (Integer) it.next();
+            assertEquals((Integer)o[0] + i, (int) x);
+            assertSame(o[i], x);
+        }
+    }
+
+    /**
+     * toArray() contains all elements in FIFO order
+     */
+    public void testToArray() {
+        ArrayDeque q = new ArrayDeque();
+        for (int i = 0; i < SIZE; i++) {
+            checkToArray(q);
+            q.addLast(i);
+        }
+        // Provoke wraparound
+        for (int i = 0; i < SIZE; i++) {
+            checkToArray(q);
+            assertEquals(i, q.poll());
+            q.addLast(SIZE + i);
+        }
+        for (int i = 0; i < SIZE; i++) {
+            checkToArray(q);
+            assertEquals(SIZE + i, q.poll());
+        }
+    }
+
+    void checkToArray2(ArrayDeque q) {
+        int size = q.size();
+        Integer[] a1 = (size == 0) ? null : new Integer[size - 1];
+        Integer[] a2 = new Integer[size];
+        Integer[] a3 = new Integer[size + 2];
+        if (size > 0) Arrays.fill(a1, 42);
+        Arrays.fill(a2, 42);
+        Arrays.fill(a3, 42);
+        Integer[] b1 = (size == 0) ? null : (Integer[]) q.toArray(a1);
+        Integer[] b2 = (Integer[]) q.toArray(a2);
+        Integer[] b3 = (Integer[]) q.toArray(a3);
+        assertSame(a2, b2);
+        assertSame(a3, b3);
+        Iterator it = q.iterator();
+        for (int i = 0; i < size; i++) {
+            Integer x = (Integer) it.next();
+            assertSame(b1[i], x);
+            assertEquals(b1[0] + i, (int) x);
+            assertSame(b2[i], x);
+            assertSame(b3[i], x);
+        }
+        assertNull(a3[size]);
+        assertEquals(42, (int) a3[size + 1]);
+        if (size > 0) {
+            assertNotSame(a1, b1);
+            assertEquals(size, b1.length);
+            for (int i = 0; i < a1.length; i++) {
+                assertEquals(42, (int) a1[i]);
+            }
+        }
+    }
+
+    /**
+     * toArray(a) contains all elements in FIFO order
+     */
+    public void testToArray2() {
+        ArrayDeque q = new ArrayDeque();
+        for (int i = 0; i < SIZE; i++) {
+            checkToArray2(q);
+            q.addLast(i);
+        }
+        // Provoke wraparound
+        for (int i = 0; i < SIZE; i++) {
+            checkToArray2(q);
+            assertEquals(i, q.poll());
+            q.addLast(SIZE + i);
+        }
+        for (int i = 0; i < SIZE; i++) {
+            checkToArray2(q);
+            assertEquals(SIZE + i, q.poll());
+        }
+    }
+
+    /**
+     * toArray(null) throws NullPointerException
+     */
+    public void testToArray_NullArg() {
+        ArrayDeque l = new ArrayDeque();
+        l.add(new Object());
+        try {
+            l.toArray(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray1_BadArg() {
+        ArrayDeque l = new ArrayDeque();
+        l.add(new Integer(5));
+        try {
+            l.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * Iterator iterates through all elements
+     */
+    public void testIterator() {
+        ArrayDeque q = populatedDeque(SIZE);
+        Iterator it = q.iterator();
+        int i;
+        for (i = 0; it.hasNext(); i++)
+            assertTrue(q.contains(it.next()));
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty collection has no elements
+     */
+    public void testEmptyIterator() {
+        Deque c = new ArrayDeque();
+        assertIteratorExhausted(c.iterator());
+        assertIteratorExhausted(c.descendingIterator());
+    }
+
+    /**
+     * Iterator ordering is FIFO
+     */
+    public void testIteratorOrdering() {
+        final ArrayDeque q = new ArrayDeque();
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        int k = 0;
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            assertEquals(++k, it.next());
+        }
+
+        assertEquals(3, k);
+    }
+
+    /**
+     * iterator.remove() removes current element
+     */
+    public void testIteratorRemove() {
+        final ArrayDeque q = new ArrayDeque();
+        final Random rng = new Random();
+        for (int iters = 0; iters < 100; ++iters) {
+            int max = rng.nextInt(5) + 2;
+            int split = rng.nextInt(max - 1) + 1;
+            for (int j = 1; j <= max; ++j)
+                q.add(new Integer(j));
+            Iterator it = q.iterator();
+            for (int j = 1; j <= split; ++j)
+                assertEquals(it.next(), new Integer(j));
+            it.remove();
+            assertEquals(it.next(), new Integer(split + 1));
+            for (int j = 1; j <= split; ++j)
+                q.remove(new Integer(j));
+            it = q.iterator();
+            for (int j = split + 1; j <= max; ++j) {
+                assertEquals(it.next(), new Integer(j));
+                it.remove();
+            }
+            assertFalse(it.hasNext());
+            assertTrue(q.isEmpty());
+        }
+    }
+
+    /**
+     * Descending iterator iterates through all elements
+     */
+    public void testDescendingIterator() {
+        ArrayDeque q = populatedDeque(SIZE);
+        int i = 0;
+        Iterator it = q.descendingIterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+        assertFalse(it.hasNext());
+        try {
+            it.next();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * Descending iterator ordering is reverse FIFO
+     */
+    public void testDescendingIteratorOrdering() {
+        final ArrayDeque q = new ArrayDeque();
+        for (int iters = 0; iters < 100; ++iters) {
+            q.add(new Integer(3));
+            q.add(new Integer(2));
+            q.add(new Integer(1));
+            int k = 0;
+            for (Iterator it = q.descendingIterator(); it.hasNext();) {
+                assertEquals(++k, it.next());
+            }
+
+            assertEquals(3, k);
+            q.remove();
+            q.remove();
+            q.remove();
+        }
+    }
+
+    /**
+     * descendingIterator.remove() removes current element
+     */
+    public void testDescendingIteratorRemove() {
+        final ArrayDeque q = new ArrayDeque();
+        final Random rng = new Random();
+        for (int iters = 0; iters < 100; ++iters) {
+            int max = rng.nextInt(5) + 2;
+            int split = rng.nextInt(max - 1) + 1;
+            for (int j = max; j >= 1; --j)
+                q.add(new Integer(j));
+            Iterator it = q.descendingIterator();
+            for (int j = 1; j <= split; ++j)
+                assertEquals(it.next(), new Integer(j));
+            it.remove();
+            assertEquals(it.next(), new Integer(split + 1));
+            for (int j = 1; j <= split; ++j)
+                q.remove(new Integer(j));
+            it = q.descendingIterator();
+            for (int j = split + 1; j <= max; ++j) {
+                assertEquals(it.next(), new Integer(j));
+                it.remove();
+            }
+            assertFalse(it.hasNext());
+            assertTrue(q.isEmpty());
+        }
+    }
+
+    /**
+     * toString() contains toStrings of elements
+     */
+    public void testToString() {
+        ArrayDeque q = populatedDeque(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized deque has same elements in same order
+     */
+    public void testSerialization() throws Exception {
+        Queue x = populatedDeque(SIZE);
+        Queue y = serialClone(x);
+
+        assertNotSame(y, x);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertTrue(Arrays.equals(x.toArray(), y.toArray()));
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(), y.remove());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * remove(null), contains(null) always return false
+     */
+    public void testNeverContainsNull() {
+        Deque<?>[] qs = {
+            new ArrayDeque<Object>(),
+            populatedDeque(2),
+        };
+
+        for (Deque<?> q : qs) {
+            assertFalse(q.contains(null));
+            assertFalse(q.remove(null));
+            assertFalse(q.removeFirstOccurrence(null));
+            assertFalse(q.removeLastOccurrence(null));
+        }
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/Atomic8Test.java b/jdk/test/java/util/concurrent/tck/Atomic8Test.java
new file mode 100644
index 00000000000..1e49299c21a
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/Atomic8Test.java
@@ -0,0 +1,596 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea and Martin Buchholz with assistance from
+ * members of JCP JSR-166 Expert Group and released to the public
+ * domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicIntegerArray;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicLongArray;
+import java.util.concurrent.atomic.AtomicLongFieldUpdater;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.atomic.AtomicReferenceArray;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class Atomic8Test extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(Atomic8Test.class);
+    }
+
+    /*
+     * Tests of atomic class methods accepting lambdas
+     * introduced in JDK8.
+     */
+
+    static long addLong17(long x) { return x + 17; }
+    static int addInt17(int x) { return x + 17; }
+    static Integer addInteger17(Integer x) {
+        return new Integer(x.intValue() + 17);
+    }
+    static Integer sumInteger(Integer x, Integer y) {
+        return new Integer(x.intValue() + y.intValue());
+    }
+
+    volatile long aLongField;
+    volatile int anIntField;
+    volatile Integer anIntegerField;
+
+    AtomicLongFieldUpdater aLongFieldUpdater() {
+        return AtomicLongFieldUpdater.newUpdater
+            (Atomic8Test.class, "aLongField");
+    }
+
+    AtomicIntegerFieldUpdater anIntFieldUpdater() {
+        return AtomicIntegerFieldUpdater.newUpdater
+            (Atomic8Test.class, "anIntField");
+    }
+
+    AtomicReferenceFieldUpdater<Atomic8Test,Integer> anIntegerFieldUpdater() {
+        return AtomicReferenceFieldUpdater.newUpdater
+            (Atomic8Test.class, Integer.class, "anIntegerField");
+    }
+
+    /**
+     * AtomicLong getAndUpdate returns previous value and updates
+     * result of supplied function
+     */
+    public void testLongGetAndUpdate() {
+        AtomicLong a = new AtomicLong(1L);
+        assertEquals(1L, a.getAndUpdate(Atomic8Test::addLong17));
+        assertEquals(18L, a.getAndUpdate(Atomic8Test::addLong17));
+        assertEquals(35L, a.get());
+    }
+
+    /**
+     * AtomicLong updateAndGet updates with supplied function and
+     * returns result.
+     */
+    public void testLongUpdateAndGet() {
+        AtomicLong a = new AtomicLong(1L);
+        assertEquals(18L, a.updateAndGet(Atomic8Test::addLong17));
+        assertEquals(35L, a.updateAndGet(Atomic8Test::addLong17));
+    }
+
+    /**
+     * AtomicLong getAndAccumulate returns previous value and updates
+     * with supplied function.
+     */
+    public void testLongGetAndAccumulate() {
+        AtomicLong a = new AtomicLong(1L);
+        assertEquals(1L, a.getAndAccumulate(2L, Long::sum));
+        assertEquals(3L, a.getAndAccumulate(3L, Long::sum));
+        assertEquals(6L, a.get());
+    }
+
+    /**
+     * AtomicLong accumulateAndGet updates with supplied function and
+     * returns result.
+     */
+    public void testLongAccumulateAndGet() {
+        AtomicLong a = new AtomicLong(1L);
+        assertEquals(7L, a.accumulateAndGet(6L, Long::sum));
+        assertEquals(10L, a.accumulateAndGet(3L, Long::sum));
+        assertEquals(10L, a.get());
+    }
+
+    /**
+     * AtomicInteger getAndUpdate returns previous value and updates
+     * result of supplied function
+     */
+    public void testIntGetAndUpdate() {
+        AtomicInteger a = new AtomicInteger(1);
+        assertEquals(1, a.getAndUpdate(Atomic8Test::addInt17));
+        assertEquals(18, a.getAndUpdate(Atomic8Test::addInt17));
+        assertEquals(35, a.get());
+    }
+
+    /**
+     * AtomicInteger updateAndGet updates with supplied function and
+     * returns result.
+     */
+    public void testIntUpdateAndGet() {
+        AtomicInteger a = new AtomicInteger(1);
+        assertEquals(18, a.updateAndGet(Atomic8Test::addInt17));
+        assertEquals(35, a.updateAndGet(Atomic8Test::addInt17));
+        assertEquals(35, a.get());
+    }
+
+    /**
+     * AtomicInteger getAndAccumulate returns previous value and updates
+     * with supplied function.
+     */
+    public void testIntGetAndAccumulate() {
+        AtomicInteger a = new AtomicInteger(1);
+        assertEquals(1, a.getAndAccumulate(2, Integer::sum));
+        assertEquals(3, a.getAndAccumulate(3, Integer::sum));
+        assertEquals(6, a.get());
+    }
+
+    /**
+     * AtomicInteger accumulateAndGet updates with supplied function and
+     * returns result.
+     */
+    public void testIntAccumulateAndGet() {
+        AtomicInteger a = new AtomicInteger(1);
+        assertEquals(7, a.accumulateAndGet(6, Integer::sum));
+        assertEquals(10, a.accumulateAndGet(3, Integer::sum));
+        assertEquals(10, a.get());
+    }
+
+    /**
+     * AtomicReference getAndUpdate returns previous value and updates
+     * result of supplied function
+     */
+    public void testReferenceGetAndUpdate() {
+        AtomicReference<Integer> a = new AtomicReference<Integer>(one);
+        assertEquals(new Integer(1), a.getAndUpdate(Atomic8Test::addInteger17));
+        assertEquals(new Integer(18), a.getAndUpdate(Atomic8Test::addInteger17));
+        assertEquals(new Integer(35), a.get());
+    }
+
+    /**
+     * AtomicReference updateAndGet updates with supplied function and
+     * returns result.
+     */
+    public void testReferenceUpdateAndGet() {
+        AtomicReference<Integer> a = new AtomicReference<Integer>(one);
+        assertEquals(new Integer(18), a.updateAndGet(Atomic8Test::addInteger17));
+        assertEquals(new Integer(35), a.updateAndGet(Atomic8Test::addInteger17));
+        assertEquals(new Integer(35), a.get());
+    }
+
+    /**
+     * AtomicReference getAndAccumulate returns previous value and updates
+     * with supplied function.
+     */
+    public void testReferenceGetAndAccumulate() {
+        AtomicReference<Integer> a = new AtomicReference<Integer>(one);
+        assertEquals(new Integer(1), a.getAndAccumulate(2, Atomic8Test::sumInteger));
+        assertEquals(new Integer(3), a.getAndAccumulate(3, Atomic8Test::sumInteger));
+        assertEquals(new Integer(6), a.get());
+    }
+
+    /**
+     * AtomicReference accumulateAndGet updates with supplied function and
+     * returns result.
+     */
+    public void testReferenceAccumulateAndGet() {
+        AtomicReference<Integer> a = new AtomicReference<Integer>(one);
+        assertEquals(new Integer(7), a.accumulateAndGet(6, Atomic8Test::sumInteger));
+        assertEquals(new Integer(10), a.accumulateAndGet(3, Atomic8Test::sumInteger));
+        assertEquals(new Integer(10), a.get());
+    }
+
+    /**
+     * AtomicLongArray getAndUpdate returns previous value and updates
+     * result of supplied function
+     */
+    public void testLongArrayGetAndUpdate() {
+        AtomicLongArray a = new AtomicLongArray(1);
+        a.set(0, 1);
+        assertEquals(1L, a.getAndUpdate(0, Atomic8Test::addLong17));
+        assertEquals(18L, a.getAndUpdate(0, Atomic8Test::addLong17));
+        assertEquals(35L, a.get(0));
+    }
+
+    /**
+     * AtomicLongArray updateAndGet updates with supplied function and
+     * returns result.
+     */
+    public void testLongArrayUpdateAndGet() {
+        AtomicLongArray a = new AtomicLongArray(1);
+        a.set(0, 1);
+        assertEquals(18L, a.updateAndGet(0, Atomic8Test::addLong17));
+        assertEquals(35L, a.updateAndGet(0, Atomic8Test::addLong17));
+        assertEquals(35L, a.get(0));
+    }
+
+    /**
+     * AtomicLongArray getAndAccumulate returns previous value and updates
+     * with supplied function.
+     */
+    public void testLongArrayGetAndAccumulate() {
+        AtomicLongArray a = new AtomicLongArray(1);
+        a.set(0, 1);
+        assertEquals(1L, a.getAndAccumulate(0, 2L, Long::sum));
+        assertEquals(3L, a.getAndAccumulate(0, 3L, Long::sum));
+        assertEquals(6L, a.get(0));
+    }
+
+    /**
+     * AtomicLongArray accumulateAndGet updates with supplied function and
+     * returns result.
+     */
+    public void testLongArrayAccumulateAndGet() {
+        AtomicLongArray a = new AtomicLongArray(1);
+        a.set(0, 1);
+        assertEquals(7L, a.accumulateAndGet(0, 6L, Long::sum));
+        assertEquals(10L, a.accumulateAndGet(0, 3L, Long::sum));
+        assertEquals(10L, a.get(0));
+    }
+
+    /**
+     * AtomicIntegerArray getAndUpdate returns previous value and updates
+     * result of supplied function
+     */
+    public void testIntArrayGetAndUpdate() {
+        AtomicIntegerArray a = new AtomicIntegerArray(1);
+        a.set(0, 1);
+        assertEquals(1, a.getAndUpdate(0, Atomic8Test::addInt17));
+        assertEquals(18, a.getAndUpdate(0, Atomic8Test::addInt17));
+        assertEquals(35, a.get(0));
+    }
+
+    /**
+     * AtomicIntegerArray updateAndGet updates with supplied function and
+     * returns result.
+     */
+    public void testIntArrayUpdateAndGet() {
+        AtomicIntegerArray a = new AtomicIntegerArray(1);
+        a.set(0, 1);
+        assertEquals(18, a.updateAndGet(0, Atomic8Test::addInt17));
+        assertEquals(35, a.updateAndGet(0, Atomic8Test::addInt17));
+        assertEquals(35, a.get(0));
+    }
+
+    /**
+     * AtomicIntegerArray getAndAccumulate returns previous value and updates
+     * with supplied function.
+     */
+    public void testIntArrayGetAndAccumulate() {
+        AtomicIntegerArray a = new AtomicIntegerArray(1);
+        a.set(0, 1);
+        assertEquals(1, a.getAndAccumulate(0, 2, Integer::sum));
+        assertEquals(3, a.getAndAccumulate(0, 3, Integer::sum));
+        assertEquals(6, a.get(0));
+    }
+
+    /**
+     * AtomicIntegerArray accumulateAndGet updates with supplied function and
+     * returns result.
+     */
+    public void testIntArrayAccumulateAndGet() {
+        AtomicIntegerArray a = new AtomicIntegerArray(1);
+        a.set(0, 1);
+        assertEquals(7, a.accumulateAndGet(0, 6, Integer::sum));
+        assertEquals(10, a.accumulateAndGet(0, 3, Integer::sum));
+    }
+
+    /**
+     * AtomicReferenceArray getAndUpdate returns previous value and updates
+     * result of supplied function
+     */
+    public void testReferenceArrayGetAndUpdate() {
+        AtomicReferenceArray<Integer> a = new AtomicReferenceArray<Integer>(1);
+        a.set(0, one);
+        assertEquals(new Integer(1), a.getAndUpdate(0, Atomic8Test::addInteger17));
+        assertEquals(new Integer(18), a.getAndUpdate(0, Atomic8Test::addInteger17));
+        assertEquals(new Integer(35), a.get(0));
+    }
+
+    /**
+     * AtomicReferenceArray updateAndGet updates with supplied function and
+     * returns result.
+     */
+    public void testReferenceArrayUpdateAndGet() {
+        AtomicReferenceArray<Integer> a = new AtomicReferenceArray<Integer>(1);
+        a.set(0, one);
+        assertEquals(new Integer(18), a.updateAndGet(0, Atomic8Test::addInteger17));
+        assertEquals(new Integer(35), a.updateAndGet(0, Atomic8Test::addInteger17));
+    }
+
+    /**
+     * AtomicReferenceArray getAndAccumulate returns previous value and updates
+     * with supplied function.
+     */
+    public void testReferenceArrayGetAndAccumulate() {
+        AtomicReferenceArray<Integer> a = new AtomicReferenceArray<Integer>(1);
+        a.set(0, one);
+        assertEquals(new Integer(1), a.getAndAccumulate(0, 2, Atomic8Test::sumInteger));
+        assertEquals(new Integer(3), a.getAndAccumulate(0, 3, Atomic8Test::sumInteger));
+        assertEquals(new Integer(6), a.get(0));
+    }
+
+    /**
+     * AtomicReferenceArray accumulateAndGet updates with supplied function and
+     * returns result.
+     */
+    public void testReferenceArrayAccumulateAndGet() {
+        AtomicReferenceArray<Integer> a = new AtomicReferenceArray<Integer>(1);
+        a.set(0, one);
+        assertEquals(new Integer(7), a.accumulateAndGet(0, 6, Atomic8Test::sumInteger));
+        assertEquals(new Integer(10), a.accumulateAndGet(0, 3, Atomic8Test::sumInteger));
+    }
+
+    /**
+     * AtomicLongFieldUpdater getAndUpdate returns previous value and updates
+     * result of supplied function
+     */
+    public void testLongFieldUpdaterGetAndUpdate() {
+        AtomicLongFieldUpdater a = aLongFieldUpdater();
+        a.set(this, 1);
+        assertEquals(1L, a.getAndUpdate(this, Atomic8Test::addLong17));
+        assertEquals(18L, a.getAndUpdate(this, Atomic8Test::addLong17));
+        assertEquals(35L, a.get(this));
+        assertEquals(35L, aLongField);
+    }
+
+    /**
+     * AtomicLongFieldUpdater updateAndGet updates with supplied function and
+     * returns result.
+     */
+    public void testLongFieldUpdaterUpdateAndGet() {
+        AtomicLongFieldUpdater a = aLongFieldUpdater();
+        a.set(this, 1);
+        assertEquals(18L, a.updateAndGet(this, Atomic8Test::addLong17));
+        assertEquals(35L, a.updateAndGet(this, Atomic8Test::addLong17));
+        assertEquals(35L, a.get(this));
+        assertEquals(35L, aLongField);
+    }
+
+    /**
+     * AtomicLongFieldUpdater getAndAccumulate returns previous value
+     * and updates with supplied function.
+     */
+    public void testLongFieldUpdaterGetAndAccumulate() {
+        AtomicLongFieldUpdater a = aLongFieldUpdater();
+        a.set(this, 1);
+        assertEquals(1L, a.getAndAccumulate(this, 2L, Long::sum));
+        assertEquals(3L, a.getAndAccumulate(this, 3L, Long::sum));
+        assertEquals(6L, a.get(this));
+        assertEquals(6L, aLongField);
+    }
+
+    /**
+     * AtomicLongFieldUpdater accumulateAndGet updates with supplied
+     * function and returns result.
+     */
+    public void testLongFieldUpdaterAccumulateAndGet() {
+        AtomicLongFieldUpdater a = aLongFieldUpdater();
+        a.set(this, 1);
+        assertEquals(7L, a.accumulateAndGet(this, 6L, Long::sum));
+        assertEquals(10L, a.accumulateAndGet(this, 3L, Long::sum));
+        assertEquals(10L, a.get(this));
+        assertEquals(10L, aLongField);
+    }
+
+    /**
+     * AtomicIntegerFieldUpdater getAndUpdate returns previous value and updates
+     * result of supplied function
+     */
+    public void testIntegerFieldUpdaterGetAndUpdate() {
+        AtomicIntegerFieldUpdater a = anIntFieldUpdater();
+        a.set(this, 1);
+        assertEquals(1, a.getAndUpdate(this, Atomic8Test::addInt17));
+        assertEquals(18, a.getAndUpdate(this, Atomic8Test::addInt17));
+        assertEquals(35, a.get(this));
+        assertEquals(35, anIntField);
+    }
+
+    /**
+     * AtomicIntegerFieldUpdater updateAndGet updates with supplied function and
+     * returns result.
+     */
+    public void testIntegerFieldUpdaterUpdateAndGet() {
+        AtomicIntegerFieldUpdater a = anIntFieldUpdater();
+        a.set(this, 1);
+        assertEquals(18, a.updateAndGet(this, Atomic8Test::addInt17));
+        assertEquals(35, a.updateAndGet(this, Atomic8Test::addInt17));
+        assertEquals(35, a.get(this));
+        assertEquals(35, anIntField);
+    }
+
+    /**
+     * AtomicIntegerFieldUpdater getAndAccumulate returns previous value
+     * and updates with supplied function.
+     */
+    public void testIntegerFieldUpdaterGetAndAccumulate() {
+        AtomicIntegerFieldUpdater a = anIntFieldUpdater();
+        a.set(this, 1);
+        assertEquals(1, a.getAndAccumulate(this, 2, Integer::sum));
+        assertEquals(3, a.getAndAccumulate(this, 3, Integer::sum));
+        assertEquals(6, a.get(this));
+        assertEquals(6, anIntField);
+    }
+
+    /**
+     * AtomicIntegerFieldUpdater accumulateAndGet updates with supplied
+     * function and returns result.
+     */
+    public void testIntegerFieldUpdaterAccumulateAndGet() {
+        AtomicIntegerFieldUpdater a = anIntFieldUpdater();
+        a.set(this, 1);
+        assertEquals(7, a.accumulateAndGet(this, 6, Integer::sum));
+        assertEquals(10, a.accumulateAndGet(this, 3, Integer::sum));
+        assertEquals(10, a.get(this));
+        assertEquals(10, anIntField);
+    }
+
+    /**
+     * AtomicReferenceFieldUpdater getAndUpdate returns previous value
+     * and updates result of supplied function
+     */
+    public void testReferenceFieldUpdaterGetAndUpdate() {
+        AtomicReferenceFieldUpdater<Atomic8Test,Integer> a = anIntegerFieldUpdater();
+        a.set(this, one);
+        assertEquals(new Integer(1), a.getAndUpdate(this, Atomic8Test::addInteger17));
+        assertEquals(new Integer(18), a.getAndUpdate(this, Atomic8Test::addInteger17));
+        assertEquals(new Integer(35), a.get(this));
+        assertEquals(new Integer(35), anIntegerField);
+    }
+
+    /**
+     * AtomicReferenceFieldUpdater updateAndGet updates with supplied
+     * function and returns result.
+     */
+    public void testReferenceFieldUpdaterUpdateAndGet() {
+        AtomicReferenceFieldUpdater<Atomic8Test,Integer> a = anIntegerFieldUpdater();
+        a.set(this, one);
+        assertEquals(new Integer(18), a.updateAndGet(this, Atomic8Test::addInteger17));
+        assertEquals(new Integer(35), a.updateAndGet(this, Atomic8Test::addInteger17));
+        assertEquals(new Integer(35), a.get(this));
+        assertEquals(new Integer(35), anIntegerField);
+    }
+
+    /**
+     * AtomicReferenceFieldUpdater returns previous value and updates
+     * with supplied function.
+     */
+    public void testReferenceFieldUpdaterGetAndAccumulate() {
+        AtomicReferenceFieldUpdater<Atomic8Test,Integer> a = anIntegerFieldUpdater();
+        a.set(this, one);
+        assertEquals(new Integer(1), a.getAndAccumulate(this, 2, Atomic8Test::sumInteger));
+        assertEquals(new Integer(3), a.getAndAccumulate(this, 3, Atomic8Test::sumInteger));
+        assertEquals(new Integer(6), a.get(this));
+        assertEquals(new Integer(6), anIntegerField);
+    }
+
+    /**
+     * AtomicReferenceFieldUpdater accumulateAndGet updates with
+     * supplied function and returns result.
+     */
+    public void testReferenceFieldUpdaterAccumulateAndGet() {
+        AtomicReferenceFieldUpdater<Atomic8Test,Integer> a = anIntegerFieldUpdater();
+        a.set(this, one);
+        assertEquals(new Integer(7), a.accumulateAndGet(this, 6, Atomic8Test::sumInteger));
+        assertEquals(new Integer(10), a.accumulateAndGet(this, 3, Atomic8Test::sumInteger));
+        assertEquals(new Integer(10), a.get(this));
+        assertEquals(new Integer(10), anIntegerField);
+    }
+
+    /**
+     * All Atomic getAndUpdate methods throw NullPointerException on
+     * null function argument
+     */
+    public void testGetAndUpdateNPE() {
+        Runnable[] throwingActions = {
+            () -> new AtomicLong().getAndUpdate(null),
+            () -> new AtomicInteger().getAndUpdate(null),
+            () -> new AtomicReference().getAndUpdate(null),
+            () -> new AtomicLongArray(1).getAndUpdate(0, null),
+            () -> new AtomicIntegerArray(1).getAndUpdate(0, null),
+            () -> new AtomicReferenceArray(1).getAndUpdate(0, null),
+            () -> aLongFieldUpdater().getAndUpdate(this, null),
+            () -> anIntFieldUpdater().getAndUpdate(this, null),
+            () -> anIntegerFieldUpdater().getAndUpdate(this, null),
+            ////() -> aLongFieldUpdater().getAndUpdate(null, Atomic8Test::addLong17),
+            ////() -> anIntFieldUpdater().getAndUpdate(null, Atomic8Test::addInt17),
+            ////() -> anIntegerFieldUpdater().getAndUpdate(null, Atomic8Test::addInteger17),
+        };
+        assertThrows(NullPointerException.class, throwingActions);
+    }
+
+    /**
+     * All Atomic updateAndGet methods throw NullPointerException on null function argument
+     */
+    public void testUpdateAndGetNPE() {
+        Runnable[] throwingActions = {
+            () -> new AtomicLong().updateAndGet(null),
+            () -> new AtomicInteger().updateAndGet(null),
+            () -> new AtomicReference().updateAndGet(null),
+            () -> new AtomicLongArray(1).updateAndGet(0, null),
+            () -> new AtomicIntegerArray(1).updateAndGet(0, null),
+            () -> new AtomicReferenceArray(1).updateAndGet(0, null),
+            () -> aLongFieldUpdater().updateAndGet(this, null),
+            () -> anIntFieldUpdater().updateAndGet(this, null),
+            () -> anIntegerFieldUpdater().updateAndGet(this, null),
+        };
+        assertThrows(NullPointerException.class, throwingActions);
+    }
+
+    /**
+     * All Atomic getAndAccumulate methods throw NullPointerException
+     * on null function argument
+     */
+    public void testGetAndAccumulateNPE() {
+        Runnable[] throwingActions = {
+            () -> new AtomicLong().getAndAccumulate(1L, null),
+            () -> new AtomicInteger().getAndAccumulate(1, null),
+            () -> new AtomicReference().getAndAccumulate(one, null),
+            () -> new AtomicLongArray(1).getAndAccumulate(0, 1L, null),
+            () -> new AtomicIntegerArray(1).getAndAccumulate(0, 1, null),
+            () -> new AtomicReferenceArray(1).getAndAccumulate(0, one, null),
+            () -> aLongFieldUpdater().getAndAccumulate(this, 1L, null),
+            () -> anIntFieldUpdater().getAndAccumulate(this, 1, null),
+            () -> anIntegerFieldUpdater().getAndAccumulate(this, one, null),
+        };
+        assertThrows(NullPointerException.class, throwingActions);
+    }
+
+    /**
+     * All Atomic accumulateAndGet methods throw NullPointerException
+     * on null function argument
+     */
+    public void testAccumulateAndGetNPE() {
+        Runnable[] throwingActions = {
+            () -> new AtomicLong().accumulateAndGet(1L, null),
+            () -> new AtomicInteger().accumulateAndGet(1, null),
+            () -> new AtomicReference().accumulateAndGet(one, null),
+            () -> new AtomicLongArray(1).accumulateAndGet(0, 1L, null),
+            () -> new AtomicIntegerArray(1).accumulateAndGet(0, 1, null),
+            () -> new AtomicReferenceArray(1).accumulateAndGet(0, one, null),
+            () -> aLongFieldUpdater().accumulateAndGet(this, 1L, null),
+            () -> anIntFieldUpdater().accumulateAndGet(this, 1, null),
+            () -> anIntegerFieldUpdater().accumulateAndGet(this, one, null),
+        };
+        assertThrows(NullPointerException.class, throwingActions);
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/AtomicBooleanTest.java b/jdk/test/java/util/concurrent/tck/AtomicBooleanTest.java
new file mode 100644
index 00000000000..91b5cfa8c1f
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/AtomicBooleanTest.java
@@ -0,0 +1,169 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicBooleanTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AtomicBooleanTest.class);
+    }
+
+    /**
+     * constructor initializes to given value
+     */
+    public void testConstructor() {
+        assertTrue(new AtomicBoolean(true).get());
+        assertFalse(new AtomicBoolean(false).get());
+    }
+
+    /**
+     * default constructed initializes to false
+     */
+    public void testConstructor2() {
+        AtomicBoolean ai = new AtomicBoolean();
+        assertFalse(ai.get());
+    }
+
+    /**
+     * get returns the last value set
+     */
+    public void testGetSet() {
+        AtomicBoolean ai = new AtomicBoolean(true);
+        assertTrue(ai.get());
+        ai.set(false);
+        assertFalse(ai.get());
+        ai.set(true);
+        assertTrue(ai.get());
+    }
+
+    /**
+     * get returns the last value lazySet in same thread
+     */
+    public void testGetLazySet() {
+        AtomicBoolean ai = new AtomicBoolean(true);
+        assertTrue(ai.get());
+        ai.lazySet(false);
+        assertFalse(ai.get());
+        ai.lazySet(true);
+        assertTrue(ai.get());
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicBoolean ai = new AtomicBoolean(true);
+        assertTrue(ai.compareAndSet(true, false));
+        assertFalse(ai.get());
+        assertTrue(ai.compareAndSet(false, false));
+        assertFalse(ai.get());
+        assertFalse(ai.compareAndSet(true, false));
+        assertFalse(ai.get());
+        assertTrue(ai.compareAndSet(false, true));
+        assertTrue(ai.get());
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        final AtomicBoolean ai = new AtomicBoolean(true);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!ai.compareAndSet(false, true)) Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(ai.compareAndSet(true, false));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicBoolean ai = new AtomicBoolean(true);
+        do {} while (!ai.weakCompareAndSet(true, false));
+        assertFalse(ai.get());
+        do {} while (!ai.weakCompareAndSet(false, false));
+        assertFalse(ai.get());
+        do {} while (!ai.weakCompareAndSet(false, true));
+        assertTrue(ai.get());
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value
+     */
+    public void testGetAndSet() {
+        AtomicBoolean ai = new AtomicBoolean(true);
+        assertEquals(true, ai.getAndSet(false));
+        assertEquals(false, ai.getAndSet(false));
+        assertEquals(false, ai.getAndSet(true));
+        assertTrue(ai.get());
+    }
+
+    /**
+     * a deserialized serialized atomic holds same value
+     */
+    public void testSerialization() throws Exception {
+        AtomicBoolean x = new AtomicBoolean();
+        AtomicBoolean y = serialClone(x);
+        x.set(true);
+        AtomicBoolean z = serialClone(x);
+        assertTrue(x.get());
+        assertFalse(y.get());
+        assertTrue(z.get());
+    }
+
+    /**
+     * toString returns current value.
+     */
+    public void testToString() {
+        AtomicBoolean ai = new AtomicBoolean();
+        assertEquals(Boolean.toString(false), ai.toString());
+        ai.set(true);
+        assertEquals(Boolean.toString(true), ai.toString());
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/AtomicIntegerArrayTest.java b/jdk/test/java/util/concurrent/tck/AtomicIntegerArrayTest.java
new file mode 100644
index 00000000000..d3d8f14f5c0
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/AtomicIntegerArrayTest.java
@@ -0,0 +1,370 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicIntegerArray;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicIntegerArrayTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AtomicIntegerArrayTest.class);
+    }
+
+    /**
+     * constructor creates array of given size with all elements zero
+     */
+    public void testConstructor() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++)
+            assertEquals(0, aa.get(i));
+    }
+
+    /**
+     * constructor with null array throws NPE
+     */
+    public void testConstructor2NPE() {
+        try {
+            int[] a = null;
+            new AtomicIntegerArray(a);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * constructor with array is of same size and has all elements
+     */
+    public void testConstructor2() {
+        int[] a = { 17, 3, -42, 99, -7 };
+        AtomicIntegerArray aa = new AtomicIntegerArray(a);
+        assertEquals(a.length, aa.length());
+        for (int i = 0; i < a.length; i++)
+            assertEquals(a[i], aa.get(i));
+    }
+
+    /**
+     * get and set for out of bound indices throw IndexOutOfBoundsException
+     */
+    public void testIndexing() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int index : new int[] { -1, SIZE }) {
+            try {
+                aa.get(index);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.set(index, 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.lazySet(index, 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.compareAndSet(index, 1, 2);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.weakCompareAndSet(index, 1, 2);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.getAndAdd(index, 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.addAndGet(index, 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * get returns the last value set at index
+     */
+    public void testGetSet() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.get(i));
+            aa.set(i, 2);
+            assertEquals(2, aa.get(i));
+            aa.set(i, -3);
+            assertEquals(-3, aa.get(i));
+        }
+    }
+
+    /**
+     * get returns the last value lazySet at index by same thread
+     */
+    public void testGetLazySet() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.lazySet(i, 1);
+            assertEquals(1, aa.get(i));
+            aa.lazySet(i, 2);
+            assertEquals(2, aa.get(i));
+            aa.lazySet(i, -3);
+            assertEquals(-3, aa.get(i));
+        }
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertTrue(aa.compareAndSet(i, 1, 2));
+            assertTrue(aa.compareAndSet(i, 2, -4));
+            assertEquals(-4, aa.get(i));
+            assertFalse(aa.compareAndSet(i, -5, 7));
+            assertEquals(-4, aa.get(i));
+            assertTrue(aa.compareAndSet(i, -4, 7));
+            assertEquals(7, aa.get(i));
+        }
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        final AtomicIntegerArray a = new AtomicIntegerArray(1);
+        a.set(0, 1);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!a.compareAndSet(0, 2, 3))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(a.compareAndSet(0, 1, 2));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertEquals(3, a.get(0));
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            do {} while (!aa.weakCompareAndSet(i, 1, 2));
+            do {} while (!aa.weakCompareAndSet(i, 2, -4));
+            assertEquals(-4, aa.get(i));
+            do {} while (!aa.weakCompareAndSet(i, -4, 7));
+            assertEquals(7, aa.get(i));
+        }
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value at given index
+     */
+    public void testGetAndSet() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.getAndSet(i, 0));
+            assertEquals(0, aa.getAndSet(i, -10));
+            assertEquals(-10, aa.getAndSet(i, 1));
+        }
+    }
+
+    /**
+     * getAndAdd returns previous value and adds given value
+     */
+    public void testGetAndAdd() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.getAndAdd(i, 2));
+            assertEquals(3, aa.get(i));
+            assertEquals(3, aa.getAndAdd(i, -4));
+            assertEquals(-1, aa.get(i));
+        }
+    }
+
+    /**
+     * getAndDecrement returns previous value and decrements
+     */
+    public void testGetAndDecrement() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.getAndDecrement(i));
+            assertEquals(0, aa.getAndDecrement(i));
+            assertEquals(-1, aa.getAndDecrement(i));
+        }
+    }
+
+    /**
+     * getAndIncrement returns previous value and increments
+     */
+    public void testGetAndIncrement() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.getAndIncrement(i));
+            assertEquals(2, aa.get(i));
+            aa.set(i, -2);
+            assertEquals(-2, aa.getAndIncrement(i));
+            assertEquals(-1, aa.getAndIncrement(i));
+            assertEquals(0, aa.getAndIncrement(i));
+            assertEquals(1, aa.get(i));
+        }
+    }
+
+    /**
+     * addAndGet adds given value to current, and returns current value
+     */
+    public void testAddAndGet() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(3, aa.addAndGet(i, 2));
+            assertEquals(3, aa.get(i));
+            assertEquals(-1, aa.addAndGet(i, -4));
+            assertEquals(-1, aa.get(i));
+        }
+    }
+
+    /**
+     * decrementAndGet decrements and returns current value
+     */
+    public void testDecrementAndGet() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(0, aa.decrementAndGet(i));
+            assertEquals(-1, aa.decrementAndGet(i));
+            assertEquals(-2, aa.decrementAndGet(i));
+            assertEquals(-2, aa.get(i));
+        }
+    }
+
+    /**
+     * incrementAndGet increments and returns current value
+     */
+    public void testIncrementAndGet() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(2, aa.incrementAndGet(i));
+            assertEquals(2, aa.get(i));
+            aa.set(i, -2);
+            assertEquals(-1, aa.incrementAndGet(i));
+            assertEquals(0, aa.incrementAndGet(i));
+            assertEquals(1, aa.incrementAndGet(i));
+            assertEquals(1, aa.get(i));
+        }
+    }
+
+    class Counter extends CheckedRunnable {
+        final AtomicIntegerArray aa;
+        volatile int counts;
+        Counter(AtomicIntegerArray a) { aa = a; }
+        public void realRun() {
+            for (;;) {
+                boolean done = true;
+                for (int i = 0; i < aa.length(); i++) {
+                    int v = aa.get(i);
+                    assertTrue(v >= 0);
+                    if (v != 0) {
+                        done = false;
+                        if (aa.compareAndSet(i, v, v - 1))
+                            ++counts;
+                    }
+                }
+                if (done)
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Multiple threads using same array of counters successfully
+     * update a number of times equal to total count
+     */
+    public void testCountingInMultipleThreads() throws InterruptedException {
+        final AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        int countdown = 10000;
+        for (int i = 0; i < SIZE; i++)
+            aa.set(i, countdown);
+        Counter c1 = new Counter(aa);
+        Counter c2 = new Counter(aa);
+        Thread t1 = new Thread(c1);
+        Thread t2 = new Thread(c2);
+        t1.start();
+        t2.start();
+        t1.join();
+        t2.join();
+        assertEquals(c1.counts+c2.counts, SIZE * countdown);
+    }
+
+    /**
+     * a deserialized serialized array holds same values
+     */
+    public void testSerialization() throws Exception {
+        AtomicIntegerArray x = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++)
+            x.set(i, -i);
+        AtomicIntegerArray y = serialClone(x);
+        assertNotSame(x, y);
+        assertEquals(x.length(), y.length());
+        for (int i = 0; i < SIZE; i++) {
+            assertEquals(x.get(i), y.get(i));
+        }
+    }
+
+    /**
+     * toString returns current value.
+     */
+    public void testToString() {
+        int[] a = { 17, 3, -42, 99, -7 };
+        AtomicIntegerArray aa = new AtomicIntegerArray(a);
+        assertEquals(Arrays.toString(a), aa.toString());
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/AtomicIntegerFieldUpdaterTest.java b/jdk/test/java/util/concurrent/tck/AtomicIntegerFieldUpdaterTest.java
new file mode 100644
index 00000000000..5f7612de975
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/AtomicIntegerFieldUpdaterTest.java
@@ -0,0 +1,363 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicIntegerFieldUpdaterTest extends JSR166TestCase {
+    volatile int x = 0;
+    protected volatile int protectedField;
+    private volatile int privateField;
+    int w;
+    float z;
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AtomicIntegerFieldUpdaterTest.class);
+    }
+
+    // for testing subclass access
+    static class AtomicIntegerFieldUpdaterTestSubclass extends AtomicIntegerFieldUpdaterTest {
+        public void checkPrivateAccess() {
+            try {
+                AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a =
+                    AtomicIntegerFieldUpdater.newUpdater
+                    (AtomicIntegerFieldUpdaterTest.class, "privateField");
+                shouldThrow();
+            } catch (RuntimeException success) {
+                assertNotNull(success.getCause());
+            }
+        }
+
+        public void checkCompareAndSetProtectedSub() {
+            AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a =
+                AtomicIntegerFieldUpdater.newUpdater
+                (AtomicIntegerFieldUpdaterTest.class, "protectedField");
+            this.protectedField = 1;
+            assertTrue(a.compareAndSet(this, 1, 2));
+            assertTrue(a.compareAndSet(this, 2, -4));
+            assertEquals(-4, a.get(this));
+            assertFalse(a.compareAndSet(this, -5, 7));
+            assertEquals(-4, a.get(this));
+            assertTrue(a.compareAndSet(this, -4, 7));
+            assertEquals(7, a.get(this));
+        }
+    }
+
+    static class UnrelatedClass {
+        public void checkPackageAccess(AtomicIntegerFieldUpdaterTest obj) {
+            obj.x = 72;
+            AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a =
+                AtomicIntegerFieldUpdater.newUpdater
+                (AtomicIntegerFieldUpdaterTest.class, "x");
+            assertEquals(72, a.get(obj));
+            assertTrue(a.compareAndSet(obj, 72, 73));
+            assertEquals(73, a.get(obj));
+        }
+
+        public void checkPrivateAccess(AtomicIntegerFieldUpdaterTest obj) {
+            try {
+                AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a =
+                    AtomicIntegerFieldUpdater.newUpdater
+                    (AtomicIntegerFieldUpdaterTest.class, "privateField");
+                throw new AssertionError("should throw");
+            } catch (RuntimeException success) {
+                assertNotNull(success.getCause());
+            }
+        }
+    }
+
+    AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> updaterFor(String fieldName) {
+        return AtomicIntegerFieldUpdater.newUpdater
+            (AtomicIntegerFieldUpdaterTest.class, fieldName);
+    }
+
+    /**
+     * Construction with non-existent field throws RuntimeException
+     */
+    public void testConstructor() {
+        try {
+            updaterFor("y");
+            shouldThrow();
+        } catch (RuntimeException success) {
+            assertNotNull(success.getCause());
+        }
+    }
+
+    /**
+     * construction with field not of given type throws IllegalArgumentException
+     */
+    public void testConstructor2() {
+        try {
+            updaterFor("z");
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * construction with non-volatile field throws IllegalArgumentException
+     */
+    public void testConstructor3() {
+        try {
+            updaterFor("w");
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * construction using private field from subclass throws RuntimeException
+     */
+    public void testPrivateFieldInSubclass() {
+        AtomicIntegerFieldUpdaterTestSubclass s =
+            new AtomicIntegerFieldUpdaterTestSubclass();
+        s.checkPrivateAccess();
+    }
+
+    /**
+     * construction from unrelated class; package access is allowed,
+     * private access is not
+     */
+    public void testUnrelatedClassAccess() {
+        new UnrelatedClass().checkPackageAccess(this);
+        new UnrelatedClass().checkPrivateAccess(this);
+    }
+
+    /**
+     * get returns the last value set or assigned
+     */
+    public void testGetSet() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.get(this));
+        a.set(this, 2);
+        assertEquals(2, a.get(this));
+        a.set(this, -3);
+        assertEquals(-3, a.get(this));
+    }
+
+    /**
+     * get returns the last value lazySet by same thread
+     */
+    public void testGetLazySet() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.get(this));
+        a.lazySet(this, 2);
+        assertEquals(2, a.get(this));
+        a.lazySet(this, -3);
+        assertEquals(-3, a.get(this));
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertTrue(a.compareAndSet(this, 1, 2));
+        assertTrue(a.compareAndSet(this, 2, -4));
+        assertEquals(-4, a.get(this));
+        assertFalse(a.compareAndSet(this, -5, 7));
+        assertEquals(-4, a.get(this));
+        assertTrue(a.compareAndSet(this, -4, 7));
+        assertEquals(7, a.get(this));
+    }
+
+    /**
+     * compareAndSet succeeds in changing protected field value if
+     * equal to expected else fails
+     */
+    public void testCompareAndSetProtected() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("protectedField");
+        protectedField = 1;
+        assertTrue(a.compareAndSet(this, 1, 2));
+        assertTrue(a.compareAndSet(this, 2, -4));
+        assertEquals(-4, a.get(this));
+        assertFalse(a.compareAndSet(this, -5, 7));
+        assertEquals(-4, a.get(this));
+        assertTrue(a.compareAndSet(this, -4, 7));
+        assertEquals(7, a.get(this));
+    }
+
+    /**
+     * compareAndSet succeeds in changing protected field value if
+     * equal to expected else fails
+     */
+    public void testCompareAndSetProtectedInSubclass() {
+        AtomicIntegerFieldUpdaterTestSubclass s =
+            new AtomicIntegerFieldUpdaterTestSubclass();
+        s.checkCompareAndSetProtectedSub();
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        x = 1;
+        final AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!a.compareAndSet(AtomicIntegerFieldUpdaterTest.this, 2, 3))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(a.compareAndSet(this, 1, 2));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertEquals(3, a.get(this));
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        do {} while (!a.weakCompareAndSet(this, 1, 2));
+        do {} while (!a.weakCompareAndSet(this, 2, -4));
+        assertEquals(-4, a.get(this));
+        do {} while (!a.weakCompareAndSet(this, -4, 7));
+        assertEquals(7, a.get(this));
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value
+     */
+    public void testGetAndSet() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.getAndSet(this, 0));
+        assertEquals(0, a.getAndSet(this, -10));
+        assertEquals(-10, a.getAndSet(this, 1));
+    }
+
+    /**
+     * getAndAdd returns previous value and adds given value
+     */
+    public void testGetAndAdd() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.getAndAdd(this, 2));
+        assertEquals(3, a.get(this));
+        assertEquals(3, a.getAndAdd(this, -4));
+        assertEquals(-1, a.get(this));
+    }
+
+    /**
+     * getAndDecrement returns previous value and decrements
+     */
+    public void testGetAndDecrement() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.getAndDecrement(this));
+        assertEquals(0, a.getAndDecrement(this));
+        assertEquals(-1, a.getAndDecrement(this));
+    }
+
+    /**
+     * getAndIncrement returns previous value and increments
+     */
+    public void testGetAndIncrement() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.getAndIncrement(this));
+        assertEquals(2, a.get(this));
+        a.set(this, -2);
+        assertEquals(-2, a.getAndIncrement(this));
+        assertEquals(-1, a.getAndIncrement(this));
+        assertEquals(0, a.getAndIncrement(this));
+        assertEquals(1, a.get(this));
+    }
+
+    /**
+     * addAndGet adds given value to current, and returns current value
+     */
+    public void testAddAndGet() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(3, a.addAndGet(this, 2));
+        assertEquals(3, a.get(this));
+        assertEquals(-1, a.addAndGet(this, -4));
+        assertEquals(-1, a.get(this));
+    }
+
+    /**
+     * decrementAndGet decrements and returns current value
+     */
+    public void testDecrementAndGet() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(0, a.decrementAndGet(this));
+        assertEquals(-1, a.decrementAndGet(this));
+        assertEquals(-2, a.decrementAndGet(this));
+        assertEquals(-2, a.get(this));
+    }
+
+    /**
+     * incrementAndGet increments and returns current value
+     */
+    public void testIncrementAndGet() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(2, a.incrementAndGet(this));
+        assertEquals(2, a.get(this));
+        a.set(this, -2);
+        assertEquals(-1, a.incrementAndGet(this));
+        assertEquals(0, a.incrementAndGet(this));
+        assertEquals(1, a.incrementAndGet(this));
+        assertEquals(1, a.get(this));
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/AtomicIntegerTest.java b/jdk/test/java/util/concurrent/tck/AtomicIntegerTest.java
new file mode 100644
index 00000000000..d148788d792
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/AtomicIntegerTest.java
@@ -0,0 +1,294 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicIntegerTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AtomicIntegerTest.class);
+    }
+
+    final int[] VALUES = {
+        Integer.MIN_VALUE, -1, 0, 1, 42, Integer.MAX_VALUE,
+    };
+
+    /**
+     * constructor initializes to given value
+     */
+    public void testConstructor() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(1, ai.get());
+    }
+
+    /**
+     * default constructed initializes to zero
+     */
+    public void testConstructor2() {
+        AtomicInteger ai = new AtomicInteger();
+        assertEquals(0, ai.get());
+    }
+
+    /**
+     * get returns the last value set
+     */
+    public void testGetSet() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(1, ai.get());
+        ai.set(2);
+        assertEquals(2, ai.get());
+        ai.set(-3);
+        assertEquals(-3, ai.get());
+    }
+
+    /**
+     * get returns the last value lazySet in same thread
+     */
+    public void testGetLazySet() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(1, ai.get());
+        ai.lazySet(2);
+        assertEquals(2, ai.get());
+        ai.lazySet(-3);
+        assertEquals(-3, ai.get());
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertTrue(ai.compareAndSet(1, 2));
+        assertTrue(ai.compareAndSet(2, -4));
+        assertEquals(-4, ai.get());
+        assertFalse(ai.compareAndSet(-5, 7));
+        assertEquals(-4, ai.get());
+        assertTrue(ai.compareAndSet(-4, 7));
+        assertEquals(7, ai.get());
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        final AtomicInteger ai = new AtomicInteger(1);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!ai.compareAndSet(2, 3))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(ai.compareAndSet(1, 2));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertEquals(3, ai.get());
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicInteger ai = new AtomicInteger(1);
+        do {} while (!ai.weakCompareAndSet(1, 2));
+        do {} while (!ai.weakCompareAndSet(2, -4));
+        assertEquals(-4, ai.get());
+        do {} while (!ai.weakCompareAndSet(-4, 7));
+        assertEquals(7, ai.get());
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value
+     */
+    public void testGetAndSet() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(1, ai.getAndSet(0));
+        assertEquals(0, ai.getAndSet(-10));
+        assertEquals(-10, ai.getAndSet(1));
+    }
+
+    /**
+     * getAndAdd returns previous value and adds given value
+     */
+    public void testGetAndAdd() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(1, ai.getAndAdd(2));
+        assertEquals(3, ai.get());
+        assertEquals(3, ai.getAndAdd(-4));
+        assertEquals(-1, ai.get());
+    }
+
+    /**
+     * getAndDecrement returns previous value and decrements
+     */
+    public void testGetAndDecrement() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(1, ai.getAndDecrement());
+        assertEquals(0, ai.getAndDecrement());
+        assertEquals(-1, ai.getAndDecrement());
+    }
+
+    /**
+     * getAndIncrement returns previous value and increments
+     */
+    public void testGetAndIncrement() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(1, ai.getAndIncrement());
+        assertEquals(2, ai.get());
+        ai.set(-2);
+        assertEquals(-2, ai.getAndIncrement());
+        assertEquals(-1, ai.getAndIncrement());
+        assertEquals(0, ai.getAndIncrement());
+        assertEquals(1, ai.get());
+    }
+
+    /**
+     * addAndGet adds given value to current, and returns current value
+     */
+    public void testAddAndGet() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(3, ai.addAndGet(2));
+        assertEquals(3, ai.get());
+        assertEquals(-1, ai.addAndGet(-4));
+        assertEquals(-1, ai.get());
+    }
+
+    /**
+     * decrementAndGet decrements and returns current value
+     */
+    public void testDecrementAndGet() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(0, ai.decrementAndGet());
+        assertEquals(-1, ai.decrementAndGet());
+        assertEquals(-2, ai.decrementAndGet());
+        assertEquals(-2, ai.get());
+    }
+
+    /**
+     * incrementAndGet increments and returns current value
+     */
+    public void testIncrementAndGet() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(2, ai.incrementAndGet());
+        assertEquals(2, ai.get());
+        ai.set(-2);
+        assertEquals(-1, ai.incrementAndGet());
+        assertEquals(0, ai.incrementAndGet());
+        assertEquals(1, ai.incrementAndGet());
+        assertEquals(1, ai.get());
+    }
+
+    /**
+     * a deserialized serialized atomic holds same value
+     */
+    public void testSerialization() throws Exception {
+        AtomicInteger x = new AtomicInteger();
+        AtomicInteger y = serialClone(x);
+        assertNotSame(x, y);
+        x.set(22);
+        AtomicInteger z = serialClone(x);
+        assertEquals(22, x.get());
+        assertEquals(0, y.get());
+        assertEquals(22, z.get());
+    }
+
+    /**
+     * toString returns current value.
+     */
+    public void testToString() {
+        AtomicInteger ai = new AtomicInteger();
+        assertEquals("0", ai.toString());
+        for (int x : VALUES) {
+            ai.set(x);
+            assertEquals(Integer.toString(x), ai.toString());
+        }
+    }
+
+    /**
+     * intValue returns current value.
+     */
+    public void testIntValue() {
+        AtomicInteger ai = new AtomicInteger();
+        assertEquals(0, ai.intValue());
+        for (int x : VALUES) {
+            ai.set(x);
+            assertEquals(x, ai.intValue());
+        }
+    }
+
+    /**
+     * longValue returns current value.
+     */
+    public void testLongValue() {
+        AtomicInteger ai = new AtomicInteger();
+        assertEquals(0L, ai.longValue());
+        for (int x : VALUES) {
+            ai.set(x);
+            assertEquals((long)x, ai.longValue());
+        }
+    }
+
+    /**
+     * floatValue returns current value.
+     */
+    public void testFloatValue() {
+        AtomicInteger ai = new AtomicInteger();
+        assertEquals(0.0f, ai.floatValue());
+        for (int x : VALUES) {
+            ai.set(x);
+            assertEquals((float)x, ai.floatValue());
+        }
+    }
+
+    /**
+     * doubleValue returns current value.
+     */
+    public void testDoubleValue() {
+        AtomicInteger ai = new AtomicInteger();
+        assertEquals(0.0d, ai.doubleValue());
+        for (int x : VALUES) {
+            ai.set(x);
+            assertEquals((double)x, ai.doubleValue());
+        }
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/AtomicLongArrayTest.java b/jdk/test/java/util/concurrent/tck/AtomicLongArrayTest.java
new file mode 100644
index 00000000000..bd74addbf7f
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/AtomicLongArrayTest.java
@@ -0,0 +1,369 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicLongArray;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicLongArrayTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AtomicLongArrayTest.class);
+    }
+
+    /**
+     * constructor creates array of given size with all elements zero
+     */
+    public void testConstructor() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++)
+            assertEquals(0, aa.get(i));
+    }
+
+    /**
+     * constructor with null array throws NPE
+     */
+    public void testConstructor2NPE() {
+        try {
+            long[] a = null;
+            new AtomicLongArray(a);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * constructor with array is of same size and has all elements
+     */
+    public void testConstructor2() {
+        long[] a = { 17L, 3L, -42L, 99L, -7L };
+        AtomicLongArray aa = new AtomicLongArray(a);
+        assertEquals(a.length, aa.length());
+        for (int i = 0; i < a.length; i++)
+            assertEquals(a[i], aa.get(i));
+    }
+
+    /**
+     * get and set for out of bound indices throw IndexOutOfBoundsException
+     */
+    public void testIndexing() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int index : new int[] { -1, SIZE }) {
+            try {
+                aa.get(index);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.set(index, 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.lazySet(index, 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.compareAndSet(index, 1, 2);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.weakCompareAndSet(index, 1, 2);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.getAndAdd(index, 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.addAndGet(index, 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * get returns the last value set at index
+     */
+    public void testGetSet() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.get(i));
+            aa.set(i, 2);
+            assertEquals(2, aa.get(i));
+            aa.set(i, -3);
+            assertEquals(-3, aa.get(i));
+        }
+    }
+
+    /**
+     * get returns the last value lazySet at index by same thread
+     */
+    public void testGetLazySet() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.lazySet(i, 1);
+            assertEquals(1, aa.get(i));
+            aa.lazySet(i, 2);
+            assertEquals(2, aa.get(i));
+            aa.lazySet(i, -3);
+            assertEquals(-3, aa.get(i));
+        }
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertTrue(aa.compareAndSet(i, 1, 2));
+            assertTrue(aa.compareAndSet(i, 2, -4));
+            assertEquals(-4, aa.get(i));
+            assertFalse(aa.compareAndSet(i, -5, 7));
+            assertEquals(-4, aa.get(i));
+            assertTrue(aa.compareAndSet(i, -4, 7));
+            assertEquals(7, aa.get(i));
+        }
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws InterruptedException {
+        final AtomicLongArray a = new AtomicLongArray(1);
+        a.set(0, 1);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!a.compareAndSet(0, 2, 3))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(a.compareAndSet(0, 1, 2));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertEquals(3, a.get(0));
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            do {} while (!aa.weakCompareAndSet(i, 1, 2));
+            do {} while (!aa.weakCompareAndSet(i, 2, -4));
+            assertEquals(-4, aa.get(i));
+            do {} while (!aa.weakCompareAndSet(i, -4, 7));
+            assertEquals(7, aa.get(i));
+        }
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value at given index
+     */
+    public void testGetAndSet() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.getAndSet(i, 0));
+            assertEquals(0, aa.getAndSet(i, -10));
+            assertEquals(-10, aa.getAndSet(i, 1));
+        }
+    }
+
+    /**
+     * getAndAdd returns previous value and adds given value
+     */
+    public void testGetAndAdd() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.getAndAdd(i, 2));
+            assertEquals(3, aa.get(i));
+            assertEquals(3, aa.getAndAdd(i, -4));
+            assertEquals(-1, aa.get(i));
+        }
+    }
+
+    /**
+     * getAndDecrement returns previous value and decrements
+     */
+    public void testGetAndDecrement() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.getAndDecrement(i));
+            assertEquals(0, aa.getAndDecrement(i));
+            assertEquals(-1, aa.getAndDecrement(i));
+        }
+    }
+
+    /**
+     * getAndIncrement returns previous value and increments
+     */
+    public void testGetAndIncrement() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.getAndIncrement(i));
+            assertEquals(2, aa.get(i));
+            aa.set(i, -2);
+            assertEquals(-2, aa.getAndIncrement(i));
+            assertEquals(-1, aa.getAndIncrement(i));
+            assertEquals(0, aa.getAndIncrement(i));
+            assertEquals(1, aa.get(i));
+        }
+    }
+
+    /**
+     * addAndGet adds given value to current, and returns current value
+     */
+    public void testAddAndGet() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(3, aa.addAndGet(i, 2));
+            assertEquals(3, aa.get(i));
+            assertEquals(-1, aa.addAndGet(i, -4));
+            assertEquals(-1, aa.get(i));
+        }
+    }
+
+    /**
+     * decrementAndGet decrements and returns current value
+     */
+    public void testDecrementAndGet() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(0, aa.decrementAndGet(i));
+            assertEquals(-1, aa.decrementAndGet(i));
+            assertEquals(-2, aa.decrementAndGet(i));
+            assertEquals(-2, aa.get(i));
+        }
+    }
+
+    /**
+     * incrementAndGet increments and returns current value
+     */
+    public void testIncrementAndGet() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(2, aa.incrementAndGet(i));
+            assertEquals(2, aa.get(i));
+            aa.set(i, -2);
+            assertEquals(-1, aa.incrementAndGet(i));
+            assertEquals(0, aa.incrementAndGet(i));
+            assertEquals(1, aa.incrementAndGet(i));
+            assertEquals(1, aa.get(i));
+        }
+    }
+
+    class Counter extends CheckedRunnable {
+        final AtomicLongArray aa;
+        volatile long counts;
+        Counter(AtomicLongArray a) { aa = a; }
+        public void realRun() {
+            for (;;) {
+                boolean done = true;
+                for (int i = 0; i < aa.length(); i++) {
+                    long v = aa.get(i);
+                    assertTrue(v >= 0);
+                    if (v != 0) {
+                        done = false;
+                        if (aa.compareAndSet(i, v, v - 1))
+                            ++counts;
+                    }
+                }
+                if (done)
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Multiple threads using same array of counters successfully
+     * update a number of times equal to total count
+     */
+    public void testCountingInMultipleThreads() throws InterruptedException {
+        final AtomicLongArray aa = new AtomicLongArray(SIZE);
+        long countdown = 10000;
+        for (int i = 0; i < SIZE; i++)
+            aa.set(i, countdown);
+        Counter c1 = new Counter(aa);
+        Counter c2 = new Counter(aa);
+        Thread t1 = new Thread(c1);
+        Thread t2 = new Thread(c2);
+        t1.start();
+        t2.start();
+        t1.join();
+        t2.join();
+        assertEquals(c1.counts+c2.counts, SIZE * countdown);
+    }
+
+    /**
+     * a deserialized serialized array holds same values
+     */
+    public void testSerialization() throws Exception {
+        AtomicLongArray x = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++)
+            x.set(i, -i);
+        AtomicLongArray y = serialClone(x);
+        assertNotSame(x, y);
+        assertEquals(x.length(), y.length());
+        for (int i = 0; i < SIZE; i++) {
+            assertEquals(x.get(i), y.get(i));
+        }
+    }
+
+    /**
+     * toString returns current value.
+     */
+    public void testToString() {
+        long[] a = { 17, 3, -42, 99, -7 };
+        AtomicLongArray aa = new AtomicLongArray(a);
+        assertEquals(Arrays.toString(a), aa.toString());
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/AtomicLongFieldUpdaterTest.java b/jdk/test/java/util/concurrent/tck/AtomicLongFieldUpdaterTest.java
new file mode 100644
index 00000000000..b9dc1017017
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/AtomicLongFieldUpdaterTest.java
@@ -0,0 +1,363 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import java.util.concurrent.atomic.AtomicLongFieldUpdater;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicLongFieldUpdaterTest extends JSR166TestCase {
+    volatile long x = 0;
+    protected volatile long protectedField;
+    private volatile long privateField;
+    long w;
+    float z;
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AtomicLongFieldUpdaterTest.class);
+    }
+
+    // for testing subclass access
+    static class AtomicLongFieldUpdaterTestSubclass extends AtomicLongFieldUpdaterTest {
+        public void checkPrivateAccess() {
+            try {
+                AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a =
+                    AtomicLongFieldUpdater.newUpdater
+                    (AtomicLongFieldUpdaterTest.class, "privateField");
+                shouldThrow();
+            } catch (RuntimeException success) {
+                assertNotNull(success.getCause());
+            }
+        }
+
+        public void checkCompareAndSetProtectedSub() {
+            AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a =
+                AtomicLongFieldUpdater.newUpdater
+                (AtomicLongFieldUpdaterTest.class, "protectedField");
+            this.protectedField = 1;
+            assertTrue(a.compareAndSet(this, 1, 2));
+            assertTrue(a.compareAndSet(this, 2, -4));
+            assertEquals(-4, a.get(this));
+            assertFalse(a.compareAndSet(this, -5, 7));
+            assertEquals(-4, a.get(this));
+            assertTrue(a.compareAndSet(this, -4, 7));
+            assertEquals(7, a.get(this));
+        }
+    }
+
+    static class UnrelatedClass {
+        public void checkPackageAccess(AtomicLongFieldUpdaterTest obj) {
+            obj.x = 72L;
+            AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a =
+                AtomicLongFieldUpdater.newUpdater
+                (AtomicLongFieldUpdaterTest.class, "x");
+            assertEquals(72L, a.get(obj));
+            assertTrue(a.compareAndSet(obj, 72L, 73L));
+            assertEquals(73L, a.get(obj));
+        }
+
+        public void checkPrivateAccess(AtomicLongFieldUpdaterTest obj) {
+            try {
+                AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a =
+                    AtomicLongFieldUpdater.newUpdater
+                    (AtomicLongFieldUpdaterTest.class, "privateField");
+                throw new AssertionError("should throw");
+            } catch (RuntimeException success) {
+                assertNotNull(success.getCause());
+            }
+        }
+    }
+
+    AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> updaterFor(String fieldName) {
+        return AtomicLongFieldUpdater.newUpdater
+            (AtomicLongFieldUpdaterTest.class, fieldName);
+    }
+
+    /**
+     * Construction with non-existent field throws RuntimeException
+     */
+    public void testConstructor() {
+        try {
+            updaterFor("y");
+            shouldThrow();
+        } catch (RuntimeException success) {
+            assertNotNull(success.getCause());
+        }
+    }
+
+    /**
+     * construction with field not of given type throws IllegalArgumentException
+     */
+    public void testConstructor2() {
+        try {
+            updaterFor("z");
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * construction with non-volatile field throws IllegalArgumentException
+     */
+    public void testConstructor3() {
+        try {
+            updaterFor("w");
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * construction using private field from subclass throws RuntimeException
+     */
+    public void testPrivateFieldInSubclass() {
+        AtomicLongFieldUpdaterTestSubclass s =
+            new AtomicLongFieldUpdaterTestSubclass();
+        s.checkPrivateAccess();
+    }
+
+    /**
+     * construction from unrelated class; package access is allowed,
+     * private access is not
+     */
+    public void testUnrelatedClassAccess() {
+        new UnrelatedClass().checkPackageAccess(this);
+        new UnrelatedClass().checkPrivateAccess(this);
+    }
+
+    /**
+     * get returns the last value set or assigned
+     */
+    public void testGetSet() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.get(this));
+        a.set(this, 2);
+        assertEquals(2, a.get(this));
+        a.set(this, -3);
+        assertEquals(-3, a.get(this));
+    }
+
+    /**
+     * get returns the last value lazySet by same thread
+     */
+    public void testGetLazySet() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.get(this));
+        a.lazySet(this, 2);
+        assertEquals(2, a.get(this));
+        a.lazySet(this, -3);
+        assertEquals(-3, a.get(this));
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertTrue(a.compareAndSet(this, 1, 2));
+        assertTrue(a.compareAndSet(this, 2, -4));
+        assertEquals(-4, a.get(this));
+        assertFalse(a.compareAndSet(this, -5, 7));
+        assertEquals(-4, a.get(this));
+        assertTrue(a.compareAndSet(this, -4, 7));
+        assertEquals(7, a.get(this));
+    }
+
+    /**
+     * compareAndSet succeeds in changing protected field value if
+     * equal to expected else fails
+     */
+    public void testCompareAndSetProtected() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("protectedField");
+        protectedField = 1;
+        assertTrue(a.compareAndSet(this, 1, 2));
+        assertTrue(a.compareAndSet(this, 2, -4));
+        assertEquals(-4, a.get(this));
+        assertFalse(a.compareAndSet(this, -5, 7));
+        assertEquals(-4, a.get(this));
+        assertTrue(a.compareAndSet(this, -4, 7));
+        assertEquals(7, a.get(this));
+    }
+
+    /**
+     * compareAndSet succeeds in changing protected field value if
+     * equal to expected else fails
+     */
+    public void testCompareAndSetProtectedInSubclass() {
+        AtomicLongFieldUpdaterTestSubclass s =
+            new AtomicLongFieldUpdaterTestSubclass();
+        s.checkCompareAndSetProtectedSub();
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        x = 1;
+        final AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!a.compareAndSet(AtomicLongFieldUpdaterTest.this, 2, 3))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(a.compareAndSet(this, 1, 2));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertEquals(3, a.get(this));
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        do {} while (!a.weakCompareAndSet(this, 1, 2));
+        do {} while (!a.weakCompareAndSet(this, 2, -4));
+        assertEquals(-4, a.get(this));
+        do {} while (!a.weakCompareAndSet(this, -4, 7));
+        assertEquals(7, a.get(this));
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value
+     */
+    public void testGetAndSet() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.getAndSet(this, 0));
+        assertEquals(0, a.getAndSet(this, -10));
+        assertEquals(-10, a.getAndSet(this, 1));
+    }
+
+    /**
+     * getAndAdd returns previous value and adds given value
+     */
+    public void testGetAndAdd() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.getAndAdd(this, 2));
+        assertEquals(3, a.get(this));
+        assertEquals(3, a.getAndAdd(this, -4));
+        assertEquals(-1, a.get(this));
+    }
+
+    /**
+     * getAndDecrement returns previous value and decrements
+     */
+    public void testGetAndDecrement() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.getAndDecrement(this));
+        assertEquals(0, a.getAndDecrement(this));
+        assertEquals(-1, a.getAndDecrement(this));
+    }
+
+    /**
+     * getAndIncrement returns previous value and increments
+     */
+    public void testGetAndIncrement() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.getAndIncrement(this));
+        assertEquals(2, a.get(this));
+        a.set(this, -2);
+        assertEquals(-2, a.getAndIncrement(this));
+        assertEquals(-1, a.getAndIncrement(this));
+        assertEquals(0, a.getAndIncrement(this));
+        assertEquals(1, a.get(this));
+    }
+
+    /**
+     * addAndGet adds given value to current, and returns current value
+     */
+    public void testAddAndGet() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(3, a.addAndGet(this, 2));
+        assertEquals(3, a.get(this));
+        assertEquals(-1, a.addAndGet(this, -4));
+        assertEquals(-1, a.get(this));
+    }
+
+    /**
+     * decrementAndGet decrements and returns current value
+     */
+    public void testDecrementAndGet() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(0, a.decrementAndGet(this));
+        assertEquals(-1, a.decrementAndGet(this));
+        assertEquals(-2, a.decrementAndGet(this));
+        assertEquals(-2, a.get(this));
+    }
+
+    /**
+     * incrementAndGet increments and returns current value
+     */
+    public void testIncrementAndGet() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(2, a.incrementAndGet(this));
+        assertEquals(2, a.get(this));
+        a.set(this, -2);
+        assertEquals(-1, a.incrementAndGet(this));
+        assertEquals(0, a.incrementAndGet(this));
+        assertEquals(1, a.incrementAndGet(this));
+        assertEquals(1, a.get(this));
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/AtomicLongTest.java b/jdk/test/java/util/concurrent/tck/AtomicLongTest.java
new file mode 100644
index 00000000000..f5191af99e6
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/AtomicLongTest.java
@@ -0,0 +1,297 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicLongTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AtomicLongTest.class);
+    }
+
+    final long[] VALUES = {
+        Long.MIN_VALUE,
+        Integer.MIN_VALUE, -1, 0, 1, 42, Integer.MAX_VALUE,
+        Long.MAX_VALUE,
+    };
+
+    /**
+     * constructor initializes to given value
+     */
+    public void testConstructor() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(1, ai.get());
+    }
+
+    /**
+     * default constructed initializes to zero
+     */
+    public void testConstructor2() {
+        AtomicLong ai = new AtomicLong();
+        assertEquals(0, ai.get());
+    }
+
+    /**
+     * get returns the last value set
+     */
+    public void testGetSet() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(1, ai.get());
+        ai.set(2);
+        assertEquals(2, ai.get());
+        ai.set(-3);
+        assertEquals(-3, ai.get());
+    }
+
+    /**
+     * get returns the last value lazySet in same thread
+     */
+    public void testGetLazySet() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(1, ai.get());
+        ai.lazySet(2);
+        assertEquals(2, ai.get());
+        ai.lazySet(-3);
+        assertEquals(-3, ai.get());
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicLong ai = new AtomicLong(1);
+        assertTrue(ai.compareAndSet(1, 2));
+        assertTrue(ai.compareAndSet(2, -4));
+        assertEquals(-4, ai.get());
+        assertFalse(ai.compareAndSet(-5, 7));
+        assertEquals(-4, ai.get());
+        assertTrue(ai.compareAndSet(-4, 7));
+        assertEquals(7, ai.get());
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        final AtomicLong ai = new AtomicLong(1);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!ai.compareAndSet(2, 3))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(ai.compareAndSet(1, 2));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertEquals(3, ai.get());
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicLong ai = new AtomicLong(1);
+        do {} while (!ai.weakCompareAndSet(1, 2));
+        do {} while (!ai.weakCompareAndSet(2, -4));
+        assertEquals(-4, ai.get());
+        do {} while (!ai.weakCompareAndSet(-4, 7));
+        assertEquals(7, ai.get());
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value
+     */
+    public void testGetAndSet() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(1, ai.getAndSet(0));
+        assertEquals(0, ai.getAndSet(-10));
+        assertEquals(-10, ai.getAndSet(1));
+    }
+
+    /**
+     * getAndAdd returns previous value and adds given value
+     */
+    public void testGetAndAdd() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(1, ai.getAndAdd(2));
+        assertEquals(3, ai.get());
+        assertEquals(3, ai.getAndAdd(-4));
+        assertEquals(-1, ai.get());
+    }
+
+    /**
+     * getAndDecrement returns previous value and decrements
+     */
+    public void testGetAndDecrement() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(1, ai.getAndDecrement());
+        assertEquals(0, ai.getAndDecrement());
+        assertEquals(-1, ai.getAndDecrement());
+    }
+
+    /**
+     * getAndIncrement returns previous value and increments
+     */
+    public void testGetAndIncrement() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(1, ai.getAndIncrement());
+        assertEquals(2, ai.get());
+        ai.set(-2);
+        assertEquals(-2, ai.getAndIncrement());
+        assertEquals(-1, ai.getAndIncrement());
+        assertEquals(0, ai.getAndIncrement());
+        assertEquals(1, ai.get());
+    }
+
+    /**
+     * addAndGet adds given value to current, and returns current value
+     */
+    public void testAddAndGet() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(3, ai.addAndGet(2));
+        assertEquals(3, ai.get());
+        assertEquals(-1, ai.addAndGet(-4));
+        assertEquals(-1, ai.get());
+    }
+
+    /**
+     * decrementAndGet decrements and returns current value
+     */
+    public void testDecrementAndGet() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(0, ai.decrementAndGet());
+        assertEquals(-1, ai.decrementAndGet());
+        assertEquals(-2, ai.decrementAndGet());
+        assertEquals(-2, ai.get());
+    }
+
+    /**
+     * incrementAndGet increments and returns current value
+     */
+    public void testIncrementAndGet() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(2, ai.incrementAndGet());
+        assertEquals(2, ai.get());
+        ai.set(-2);
+        assertEquals(-1, ai.incrementAndGet());
+        assertEquals(0, ai.incrementAndGet());
+        assertEquals(1, ai.incrementAndGet());
+        assertEquals(1, ai.get());
+    }
+
+    /**
+     * a deserialized serialized atomic holds same value
+     */
+    public void testSerialization() throws Exception {
+        AtomicLong x = new AtomicLong();
+        AtomicLong y = serialClone(x);
+        assertNotSame(x, y);
+        x.set(-22);
+        AtomicLong z = serialClone(x);
+        assertNotSame(y, z);
+        assertEquals(-22, x.get());
+        assertEquals(0, y.get());
+        assertEquals(-22, z.get());
+    }
+
+    /**
+     * toString returns current value.
+     */
+    public void testToString() {
+        AtomicLong ai = new AtomicLong();
+        assertEquals("0", ai.toString());
+        for (long x : VALUES) {
+            ai.set(x);
+            assertEquals(Long.toString(x), ai.toString());
+        }
+    }
+
+    /**
+     * intValue returns current value.
+     */
+    public void testIntValue() {
+        AtomicLong ai = new AtomicLong();
+        assertEquals(0, ai.intValue());
+        for (long x : VALUES) {
+            ai.set(x);
+            assertEquals((int)x, ai.intValue());
+        }
+    }
+
+    /**
+     * longValue returns current value.
+     */
+    public void testLongValue() {
+        AtomicLong ai = new AtomicLong();
+        assertEquals(0L, ai.longValue());
+        for (long x : VALUES) {
+            ai.set(x);
+            assertEquals(x, ai.longValue());
+        }
+    }
+
+    /**
+     * floatValue returns current value.
+     */
+    public void testFloatValue() {
+        AtomicLong ai = new AtomicLong();
+        assertEquals(0.0f, ai.floatValue());
+        for (long x : VALUES) {
+            ai.set(x);
+            assertEquals((float)x, ai.floatValue());
+        }
+    }
+
+    /**
+     * doubleValue returns current value.
+     */
+    public void testDoubleValue() {
+        AtomicLong ai = new AtomicLong();
+        assertEquals(0.0d, ai.doubleValue());
+        for (long x : VALUES) {
+            ai.set(x);
+            assertEquals((double)x, ai.doubleValue());
+        }
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/AtomicMarkableReferenceTest.java b/jdk/test/java/util/concurrent/tck/AtomicMarkableReferenceTest.java
new file mode 100644
index 00000000000..b1c77c468d2
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/AtomicMarkableReferenceTest.java
@@ -0,0 +1,180 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import java.util.concurrent.atomic.AtomicMarkableReference;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicMarkableReferenceTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AtomicMarkableReferenceTest.class);
+    }
+
+    /**
+     * constructor initializes to given reference and mark
+     */
+    public void testConstructor() {
+        AtomicMarkableReference ai = new AtomicMarkableReference(one, false);
+        assertSame(one, ai.getReference());
+        assertFalse(ai.isMarked());
+        AtomicMarkableReference a2 = new AtomicMarkableReference(null, true);
+        assertNull(a2.getReference());
+        assertTrue(a2.isMarked());
+    }
+
+    /**
+     * get returns the last values of reference and mark set
+     */
+    public void testGetSet() {
+        boolean[] mark = new boolean[1];
+        AtomicMarkableReference ai = new AtomicMarkableReference(one, false);
+        assertSame(one, ai.getReference());
+        assertFalse(ai.isMarked());
+        assertSame(one, ai.get(mark));
+        assertFalse(mark[0]);
+        ai.set(two, false);
+        assertSame(two, ai.getReference());
+        assertFalse(ai.isMarked());
+        assertSame(two, ai.get(mark));
+        assertFalse(mark[0]);
+        ai.set(one, true);
+        assertSame(one, ai.getReference());
+        assertTrue(ai.isMarked());
+        assertSame(one, ai.get(mark));
+        assertTrue(mark[0]);
+    }
+
+    /**
+     * attemptMark succeeds in single thread
+     */
+    public void testAttemptMark() {
+        boolean[] mark = new boolean[1];
+        AtomicMarkableReference ai = new AtomicMarkableReference(one, false);
+        assertFalse(ai.isMarked());
+        assertTrue(ai.attemptMark(one, true));
+        assertTrue(ai.isMarked());
+        assertSame(one, ai.get(mark));
+        assertTrue(mark[0]);
+    }
+
+    /**
+     * compareAndSet succeeds in changing values if equal to expected reference
+     * and mark else fails
+     */
+    public void testCompareAndSet() {
+        boolean[] mark = new boolean[1];
+        AtomicMarkableReference ai = new AtomicMarkableReference(one, false);
+        assertSame(one, ai.get(mark));
+        assertFalse(ai.isMarked());
+        assertFalse(mark[0]);
+
+        assertTrue(ai.compareAndSet(one, two, false, false));
+        assertSame(two, ai.get(mark));
+        assertFalse(mark[0]);
+
+        assertTrue(ai.compareAndSet(two, m3, false, true));
+        assertSame(m3, ai.get(mark));
+        assertTrue(mark[0]);
+
+        assertFalse(ai.compareAndSet(two, m3, true, true));
+        assertSame(m3, ai.get(mark));
+        assertTrue(mark[0]);
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for reference value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        final AtomicMarkableReference ai = new AtomicMarkableReference(one, false);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!ai.compareAndSet(two, three, false, false))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(ai.compareAndSet(one, two, false, false));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertSame(three, ai.getReference());
+        assertFalse(ai.isMarked());
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for mark value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads2() throws Exception {
+        final AtomicMarkableReference ai = new AtomicMarkableReference(one, false);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!ai.compareAndSet(one, one, true, false))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(ai.compareAndSet(one, one, false, true));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertSame(one, ai.getReference());
+        assertFalse(ai.isMarked());
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing values when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        boolean[] mark = new boolean[1];
+        AtomicMarkableReference ai = new AtomicMarkableReference(one, false);
+        assertSame(one, ai.get(mark));
+        assertFalse(ai.isMarked());
+        assertFalse(mark[0]);
+
+        do {} while (!ai.weakCompareAndSet(one, two, false, false));
+        assertSame(two, ai.get(mark));
+        assertFalse(mark[0]);
+
+        do {} while (!ai.weakCompareAndSet(two, m3, false, true));
+        assertSame(m3, ai.get(mark));
+        assertTrue(mark[0]);
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/AtomicReferenceArrayTest.java b/jdk/test/java/util/concurrent/tck/AtomicReferenceArrayTest.java
new file mode 100644
index 00000000000..2457d40b426
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/AtomicReferenceArrayTest.java
@@ -0,0 +1,246 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicReferenceArray;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicReferenceArrayTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AtomicReferenceArrayTest.class);
+    }
+
+    /**
+     * constructor creates array of given size with all elements null
+     */
+    public void testConstructor() {
+        AtomicReferenceArray<Integer> aa = new AtomicReferenceArray<Integer>(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            assertNull(aa.get(i));
+        }
+    }
+
+    /**
+     * constructor with null array throws NPE
+     */
+    public void testConstructor2NPE() {
+        try {
+            Integer[] a = null;
+            new AtomicReferenceArray<Integer>(a);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * constructor with array is of same size and has all elements
+     */
+    public void testConstructor2() {
+        Integer[] a = { two, one, three, four, seven };
+        AtomicReferenceArray<Integer> aa = new AtomicReferenceArray<Integer>(a);
+        assertEquals(a.length, aa.length());
+        for (int i = 0; i < a.length; i++)
+            assertEquals(a[i], aa.get(i));
+    }
+
+    /**
+     * Initialize AtomicReferenceArray<Class> with SubClass[]
+     */
+    public void testConstructorSubClassArray() {
+        Integer[] a = { two, one, three, four, seven };
+        AtomicReferenceArray<Number> aa = new AtomicReferenceArray<Number>(a);
+        assertEquals(a.length, aa.length());
+        for (int i = 0; i < a.length; i++) {
+            assertSame(a[i], aa.get(i));
+            Long x = Long.valueOf(i);
+            aa.set(i, x);
+            assertSame(x, aa.get(i));
+        }
+    }
+
+    /**
+     * get and set for out of bound indices throw IndexOutOfBoundsException
+     */
+    public void testIndexing() {
+        AtomicReferenceArray<Integer> aa = new AtomicReferenceArray<Integer>(SIZE);
+        for (int index : new int[] { -1, SIZE }) {
+            try {
+                aa.get(index);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.set(index, null);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.lazySet(index, null);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.compareAndSet(index, null, null);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.weakCompareAndSet(index, null, null);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * get returns the last value set at index
+     */
+    public void testGetSet() {
+        AtomicReferenceArray aa = new AtomicReferenceArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, one);
+            assertSame(one, aa.get(i));
+            aa.set(i, two);
+            assertSame(two, aa.get(i));
+            aa.set(i, m3);
+            assertSame(m3, aa.get(i));
+        }
+    }
+
+    /**
+     * get returns the last value lazySet at index by same thread
+     */
+    public void testGetLazySet() {
+        AtomicReferenceArray aa = new AtomicReferenceArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.lazySet(i, one);
+            assertSame(one, aa.get(i));
+            aa.lazySet(i, two);
+            assertSame(two, aa.get(i));
+            aa.lazySet(i, m3);
+            assertSame(m3, aa.get(i));
+        }
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicReferenceArray aa = new AtomicReferenceArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, one);
+            assertTrue(aa.compareAndSet(i, one, two));
+            assertTrue(aa.compareAndSet(i, two, m4));
+            assertSame(m4, aa.get(i));
+            assertFalse(aa.compareAndSet(i, m5, seven));
+            assertSame(m4, aa.get(i));
+            assertTrue(aa.compareAndSet(i, m4, seven));
+            assertSame(seven, aa.get(i));
+        }
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws InterruptedException {
+        final AtomicReferenceArray a = new AtomicReferenceArray(1);
+        a.set(0, one);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!a.compareAndSet(0, two, three))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(a.compareAndSet(0, one, two));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertSame(three, a.get(0));
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicReferenceArray aa = new AtomicReferenceArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, one);
+            do {} while (!aa.weakCompareAndSet(i, one, two));
+            do {} while (!aa.weakCompareAndSet(i, two, m4));
+            assertSame(m4, aa.get(i));
+            do {} while (!aa.weakCompareAndSet(i, m4, seven));
+            assertSame(seven, aa.get(i));
+        }
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value at given index
+     */
+    public void testGetAndSet() {
+        AtomicReferenceArray aa = new AtomicReferenceArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, one);
+            assertSame(one, aa.getAndSet(i, zero));
+            assertSame(zero, aa.getAndSet(i, m10));
+            assertSame(m10, aa.getAndSet(i, one));
+        }
+    }
+
+    /**
+     * a deserialized serialized array holds same values
+     */
+    public void testSerialization() throws Exception {
+        AtomicReferenceArray x = new AtomicReferenceArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            x.set(i, new Integer(-i));
+        }
+        AtomicReferenceArray y = serialClone(x);
+        assertNotSame(x, y);
+        assertEquals(x.length(), y.length());
+        for (int i = 0; i < SIZE; i++) {
+            assertEquals(x.get(i), y.get(i));
+        }
+    }
+
+    /**
+     * toString returns current value.
+     */
+    public void testToString() {
+        Integer[] a = { two, one, three, four, seven };
+        AtomicReferenceArray<Integer> aa = new AtomicReferenceArray<Integer>(a);
+        assertEquals(Arrays.toString(a), aa.toString());
+    }
+}
diff --git a/jdk/test/java/util/concurrent/tck/AtomicReferenceFieldUpdaterTest.java b/jdk/test/java/util/concurrent/tck/AtomicReferenceFieldUpdaterTest.java
new file mode 100644
index 00000000000..fb50658ea91
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/AtomicReferenceFieldUpdaterTest.java
@@ -0,0 +1,265 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicReferenceFieldUpdaterTest extends JSR166TestCase {
+    volatile Integer x = null;
+    protected volatile Integer protectedField;
+    private volatile Integer privateField;
+    Object z;
+    Integer w;
+    volatile int i;
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AtomicReferenceFieldUpdaterTest.class);
+    }
+
+    // for testing subclass access
+    static class AtomicReferenceFieldUpdaterTestSubclass extends AtomicReferenceFieldUpdaterTest {
+        public void checkPrivateAccess() {
+            try {
+                AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest,Integer> a =
+                    AtomicReferenceFieldUpdater.newUpdater
+                    (AtomicReferenceFieldUpdaterTest.class, Integer.class, "privateField");
+                shouldThrow();
+            } catch (RuntimeException success) {
+                assertNotNull(success.getCause());
+            }
+        }
+
+        public void checkCompareAndSetProtectedSub() {
+            AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest,Integer> a =
+                AtomicReferenceFieldUpdater.newUpdater
+                (AtomicReferenceFieldUpdaterTest.class, Integer.class, "protectedField");
+            this.protectedField = one;
+            assertTrue(a.compareAndSet(this, one, two));
+            assertTrue(a.compareAndSet(this, two, m4));
+            assertSame(m4, a.get(this));
+            assertFalse(a.compareAndSet(this, m5, seven));
+            assertFalse(seven == a.get(this));
+            assertTrue(a.compareAndSet(this, m4, seven));
+            assertSame(seven, a.get(this));
+        }
+    }
+
+    static class UnrelatedClass {
+        public void checkPackageAccess(AtomicReferenceFieldUpdaterTest obj) {
+            obj.x = one;
+            AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest,Integer> a =
+                AtomicReferenceFieldUpdater.newUpdater
+                (AtomicReferenceFieldUpdaterTest.class, Integer.class, "x");
+            assertSame(one, a.get(obj));
+            assertTrue(a.compareAndSet(obj, one, two));
+            assertSame(two, a.get(obj));
+        }
+
+        public void checkPrivateAccess(AtomicReferenceFieldUpdaterTest obj) {
+            try {
+                AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest,Integer> a =
+                    AtomicReferenceFieldUpdater.newUpdater
+                    (AtomicReferenceFieldUpdaterTest.class, Integer.class, "privateField");
+                throw new AssertionError("should throw");
+            } catch (RuntimeException success) {
+                assertNotNull(success.getCause());
+            }
+        }
+    }
+
+    static AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer> updaterFor(String fieldName) {
+        return AtomicReferenceFieldUpdater.newUpdater
+            (AtomicReferenceFieldUpdaterTest.class, Integer.class, fieldName);
+    }
+
+    /**
+     * Construction with non-existent field throws RuntimeException
+     */
+    public void testConstructor() {
+        try {
+            updaterFor("y");
+            shouldThrow();
+        } catch (RuntimeException success) {
+            assertNotNull(success.getCause());
+        }
+    }
+
+    /**
+     * construction with field not of given type throws ClassCastException
+     */
+    public void testConstructor2() {
+        try {
+            updaterFor("z");
+            shouldThrow();
+        } catch (ClassCastException success) {}
+    }
+
+    /**
+     * Constructor with non-volatile field throws IllegalArgumentException
+     */
+    public void testConstructor3() {
+        try {
+            updaterFor("w");
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor with non-reference field throws ClassCastException
+     */
+    public void testConstructor4() {
+        try {
+            updaterFor("i");
+            shouldThrow();
+        } catch (ClassCastException success) {}
+    }
+
+    /**
+     * construction using private field from subclass throws RuntimeException
+     */
+    public void testPrivateFieldInSubclass() {
+        AtomicReferenceFieldUpdaterTestSubclass s =
+            new AtomicReferenceFieldUpdaterTestSubclass();
+        s.checkPrivateAccess();
+    }
+
+    /**
+     * construction from unrelated class; package access is allowed,
+     * private access is not
+     */
+    public void testUnrelatedClassAccess() {
+        new UnrelatedClass().checkPackageAccess(this);
+        new UnrelatedClass().checkPrivateAccess(this);
+    }
+
+    /**
+     * get returns the last value set or assigned
+     */
+    public void testGetSet() {
+        AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer> a;
+        a = updaterFor("x");
+        x = one;
+        assertSame(one, a.get(this));
+        a.set(this, two);
+        assertSame(two, a.get(this));
+        a.set(this, m3);
+        assertSame(m3, a.get(this));
+    }
+
+    /**
+     * get returns the last value lazySet by same thread
+     */
+    public void testGetLazySet() {
+        AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer> a;
+        a = updaterFor("x");
+        x = one;
+        assertSame(one, a.get(this));
+        a.lazySet(this, two);
+        assertSame(two, a.get(this));
+        a.lazySet(this, m3);
+        assertSame(m3, a.get(this));
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer> a;
+        a = updaterFor("x");
+        x = one;
+        assertTrue(a.compareAndSet(this, one, two));
+        assertTrue(a.compareAndSet(this, two, m4));
+        assertSame(m4, a.get(this));
+        assertFalse(a.compareAndSet(this, m5, seven));
+        assertFalse(seven == a.get(this));
+        assertTrue(a.compareAndSet(this, m4, seven));
+        assertSame(seven, a.get(this));
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        x = one;
+        final AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer> a;
+        a = updaterFor("x");
+
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!a.compareAndSet(AtomicReferenceFieldUpdaterTest.this, two, three))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(a.compareAndSet(this, one, two));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertSame(three, a.get(this));
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer> a;
+        a = updaterFor("x");
+        x = one;
+        do {} while (!a.weakCompareAndSet(this, one, two));
+        do {} while (!a.weakCompareAndSet(this, two, m4));
+        assertSame(m4, a.get(this));
+        do {} while (!a.weakCompareAndSet(this, m4, seven));
+        assertSame(seven, a.get(this));
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value
+     */
+    public void testGetAndSet() {
+        AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer> a;
+        a = updaterFor("x");
+        x = one;
+        assertSame(one, a.getAndSet(this, zero));
+        assertSame(zero, a.getAndSet(this, m10));
+        assertSame(m10, a.getAndSet(this, 1));
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/AtomicReferenceTest.java b/jdk/test/java/util/concurrent/tck/AtomicReferenceTest.java
new file mode 100644
index 00000000000..bae2a43906a
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/AtomicReferenceTest.java
@@ -0,0 +1,170 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicReferenceTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AtomicReferenceTest.class);
+    }
+
+    /**
+     * constructor initializes to given value
+     */
+    public void testConstructor() {
+        AtomicReference ai = new AtomicReference(one);
+        assertSame(one, ai.get());
+    }
+
+    /**
+     * default constructed initializes to null
+     */
+    public void testConstructor2() {
+        AtomicReference ai = new AtomicReference();
+        assertNull(ai.get());
+    }
+
+    /**
+     * get returns the last value set
+     */
+    public void testGetSet() {
+        AtomicReference ai = new AtomicReference(one);
+        assertSame(one, ai.get());
+        ai.set(two);
+        assertSame(two, ai.get());
+        ai.set(m3);
+        assertSame(m3, ai.get());
+    }
+
+    /**
+     * get returns the last value lazySet in same thread
+     */
+    public void testGetLazySet() {
+        AtomicReference ai = new AtomicReference(one);
+        assertSame(one, ai.get());
+        ai.lazySet(two);
+        assertSame(two, ai.get());
+        ai.lazySet(m3);
+        assertSame(m3, ai.get());
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicReference ai = new AtomicReference(one);
+        assertTrue(ai.compareAndSet(one, two));
+        assertTrue(ai.compareAndSet(two, m4));
+        assertSame(m4, ai.get());
+        assertFalse(ai.compareAndSet(m5, seven));
+        assertSame(m4, ai.get());
+        assertTrue(ai.compareAndSet(m4, seven));
+        assertSame(seven, ai.get());
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        final AtomicReference ai = new AtomicReference(one);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!ai.compareAndSet(two, three))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(ai.compareAndSet(one, two));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertSame(three, ai.get());
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicReference ai = new AtomicReference(one);
+        do {} while (!ai.weakCompareAndSet(one, two));
+        do {} while (!ai.weakCompareAndSet(two, m4));
+        assertSame(m4, ai.get());
+        do {} while (!ai.weakCompareAndSet(m4, seven));
+        assertSame(seven, ai.get());
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value
+     */
+    public void testGetAndSet() {
+        AtomicReference ai = new AtomicReference(one);
+        assertSame(one, ai.getAndSet(zero));
+        assertSame(zero, ai.getAndSet(m10));
+        assertSame(m10, ai.getAndSet(one));
+    }
+
+    /**
+     * a deserialized serialized atomic holds same value
+     */
+    public void testSerialization() throws Exception {
+        AtomicReference x = new AtomicReference();
+        AtomicReference y = serialClone(x);
+        assertNotSame(x, y);
+        x.set(one);
+        AtomicReference z = serialClone(x);
+        assertNotSame(y, z);
+        assertEquals(one, x.get());
+        assertEquals(null, y.get());
+        assertEquals(one, z.get());
+    }
+
+    /**
+     * toString returns current value.
+     */
+    public void testToString() {
+        AtomicReference<Integer> ai = new AtomicReference<Integer>(one);
+        assertEquals(one.toString(), ai.toString());
+        ai.set(two);
+        assertEquals(two.toString(), ai.toString());
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/AtomicStampedReferenceTest.java b/jdk/test/java/util/concurrent/tck/AtomicStampedReferenceTest.java
new file mode 100644
index 00000000000..1a352130d3e
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/AtomicStampedReferenceTest.java
@@ -0,0 +1,180 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import java.util.concurrent.atomic.AtomicStampedReference;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicStampedReferenceTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AtomicStampedReferenceTest.class);
+    }
+
+    /**
+     * constructor initializes to given reference and stamp
+     */
+    public void testConstructor() {
+        AtomicStampedReference ai = new AtomicStampedReference(one, 0);
+        assertSame(one, ai.getReference());
+        assertEquals(0, ai.getStamp());
+        AtomicStampedReference a2 = new AtomicStampedReference(null, 1);
+        assertNull(a2.getReference());
+        assertEquals(1, a2.getStamp());
+    }
+
+    /**
+     * get returns the last values of reference and stamp set
+     */
+    public void testGetSet() {
+        int[] mark = new int[1];
+        AtomicStampedReference ai = new AtomicStampedReference(one, 0);
+        assertSame(one, ai.getReference());
+        assertEquals(0, ai.getStamp());
+        assertSame(one, ai.get(mark));
+        assertEquals(0, mark[0]);
+        ai.set(two, 0);
+        assertSame(two, ai.getReference());
+        assertEquals(0, ai.getStamp());
+        assertSame(two, ai.get(mark));
+        assertEquals(0, mark[0]);
+        ai.set(one, 1);
+        assertSame(one, ai.getReference());
+        assertEquals(1, ai.getStamp());
+        assertSame(one, ai.get(mark));
+        assertEquals(1, mark[0]);
+    }
+
+    /**
+     * attemptStamp succeeds in single thread
+     */
+    public void testAttemptStamp() {
+        int[] mark = new int[1];
+        AtomicStampedReference ai = new AtomicStampedReference(one, 0);
+        assertEquals(0, ai.getStamp());
+        assertTrue(ai.attemptStamp(one, 1));
+        assertEquals(1, ai.getStamp());
+        assertSame(one, ai.get(mark));
+        assertEquals(1, mark[0]);
+    }
+
+    /**
+     * compareAndSet succeeds in changing values if equal to expected reference
+     * and stamp else fails
+     */
+    public void testCompareAndSet() {
+        int[] mark = new int[1];
+        AtomicStampedReference ai = new AtomicStampedReference(one, 0);
+        assertSame(one, ai.get(mark));
+        assertEquals(0, ai.getStamp());
+        assertEquals(0, mark[0]);
+
+        assertTrue(ai.compareAndSet(one, two, 0, 0));
+        assertSame(two, ai.get(mark));
+        assertEquals(0, mark[0]);
+
+        assertTrue(ai.compareAndSet(two, m3, 0, 1));
+        assertSame(m3, ai.get(mark));
+        assertEquals(1, mark[0]);
+
+        assertFalse(ai.compareAndSet(two, m3, 1, 1));
+        assertSame(m3, ai.get(mark));
+        assertEquals(1, mark[0]);
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for reference value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        final AtomicStampedReference ai = new AtomicStampedReference(one, 0);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!ai.compareAndSet(two, three, 0, 0))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(ai.compareAndSet(one, two, 0, 0));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertSame(three, ai.getReference());
+        assertEquals(0, ai.getStamp());
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for stamp value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads2() throws Exception {
+        final AtomicStampedReference ai = new AtomicStampedReference(one, 0);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!ai.compareAndSet(one, one, 1, 2))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(ai.compareAndSet(one, one, 0, 1));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertSame(one, ai.getReference());
+        assertEquals(2, ai.getStamp());
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing values when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        int[] mark = new int[1];
+        AtomicStampedReference ai = new AtomicStampedReference(one, 0);
+        assertSame(one, ai.get(mark));
+        assertEquals(0, ai.getStamp());
+        assertEquals(0, mark[0]);
+
+        do {} while (!ai.weakCompareAndSet(one, two, 0, 0));
+        assertSame(two, ai.get(mark));
+        assertEquals(0, mark[0]);
+
+        do {} while (!ai.weakCompareAndSet(two, m3, 0, 1));
+        assertSame(m3, ai.get(mark));
+        assertEquals(1, mark[0]);
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/BlockingQueueTest.java b/jdk/test/java/util/concurrent/tck/BlockingQueueTest.java
new file mode 100644
index 00000000000..aa38b3bd735
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/BlockingQueueTest.java
@@ -0,0 +1,403 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea and Martin Buchholz with assistance from members
+ * of JCP JSR-166 Expert Group and released to the public domain, as
+ * explained at http://creativecommons.org/publicdomain/zero/1.0/
+ *
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Queue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Contains "contract" tests applicable to all BlockingQueue implementations.
+ */
+public abstract class BlockingQueueTest extends JSR166TestCase {
+    /*
+     * This is the start of an attempt to refactor the tests for the
+     * various related implementations of related interfaces without
+     * too much duplicated code.  junit does not really support such
+     * testing.  Here subclasses of TestCase not only contain tests,
+     * but also configuration information that describes the
+     * implementation class, most importantly how to instantiate
+     * instances.
+     */
+
+    /** Like suite(), but non-static */
+    public Test testSuite() {
+        // TODO: filter the returned tests using the configuration
+        // information provided by the subclass via protected methods.
+        return new TestSuite(this.getClass());
+    }
+
+    //----------------------------------------------------------------
+    // Configuration methods
+    //----------------------------------------------------------------
+
+    /** Returns an empty instance of the implementation class. */
+    protected abstract BlockingQueue emptyCollection();
+
+    /**
+     * Returns an element suitable for insertion in the collection.
+     * Override for collections with unusual element types.
+     */
+    protected Object makeElement(int i) {
+        return Integer.valueOf(i);
+    }
+
+    //----------------------------------------------------------------
+    // Tests
+    //----------------------------------------------------------------
+
+    /**
+     * offer(null) throws NullPointerException
+     */
+    public void testOfferNull() {
+        final Queue q = emptyCollection();
+        try {
+            q.offer(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * add(null) throws NullPointerException
+     */
+    public void testAddNull() {
+        final Collection q = emptyCollection();
+        try {
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * timed offer(null) throws NullPointerException
+     */
+    public void testTimedOfferNull() throws InterruptedException {
+        final BlockingQueue q = emptyCollection();
+        long startTime = System.nanoTime();
+        try {
+            q.offer(null, LONG_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+    }
+
+    /**
+     * put(null) throws NullPointerException
+     */
+    public void testPutNull() throws InterruptedException {
+        final BlockingQueue q = emptyCollection();
+        try {
+            q.put(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * put(null) throws NullPointerException
+     */
+    public void testAddAllNull() throws InterruptedException {
+        final Collection q = emptyCollection();
+        try {
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NullPointerException
+     */
+    public void testAddAllNullElements() {
+        final Collection q = emptyCollection();
+        final Collection<Integer> elements = Arrays.asList(new Integer[SIZE]);
+        try {
+            q.addAll(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * toArray(null) throws NullPointerException
+     */
+    public void testToArray_NullArray() {
+        final Collection q = emptyCollection();
+        try {
+            q.toArray(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * drainTo(null) throws NullPointerException
+     */
+    public void testDrainToNull() {
+        final BlockingQueue q = emptyCollection();
+        try {
+            q.drainTo(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * drainTo(this) throws IllegalArgumentException
+     */
+    public void testDrainToSelf() {
+        final BlockingQueue q = emptyCollection();
+        try {
+            q.drainTo(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * drainTo(null, n) throws NullPointerException
+     */
+    public void testDrainToNullN() {
+        final BlockingQueue q = emptyCollection();
+        try {
+            q.drainTo(null, 0);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * drainTo(this, n) throws IllegalArgumentException
+     */
+    public void testDrainToSelfN() {
+        final BlockingQueue q = emptyCollection();
+        try {
+            q.drainTo(q, 0);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * drainTo(c, n) returns 0 and does nothing when n <= 0
+     */
+    public void testDrainToNonPositiveMaxElements() {
+        final BlockingQueue q = emptyCollection();
+        final int[] ns = { 0, -1, -42, Integer.MIN_VALUE };
+        for (int n : ns)
+            assertEquals(0, q.drainTo(new ArrayList(), n));
+        if (q.remainingCapacity() > 0) {
+            // Not SynchronousQueue, that is
+            Object one = makeElement(1);
+            q.add(one);
+            ArrayList c = new ArrayList();
+            for (int n : ns)
+                assertEquals(0, q.drainTo(new ArrayList(), n));
+            assertEquals(1, q.size());
+            assertSame(one, q.poll());
+            assertTrue(c.isEmpty());
+        }
+    }
+
+    /**
+     * timed poll before a delayed offer times out; after offer succeeds;
+     * on interruption throws
+     */
+    public void testTimedPollWithOffer() throws InterruptedException {
+        final BlockingQueue q = emptyCollection();
+        final CheckedBarrier barrier = new CheckedBarrier(2);
+        final Object zero = makeElement(0);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+
+                barrier.await();
+
+                assertSame(zero, q.poll(LONG_DELAY_MS, MILLISECONDS));
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                barrier.await();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+            }});
+
+        barrier.await();
+        long startTime = System.nanoTime();
+        assertTrue(q.offer(zero, LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+
+        barrier.await();
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * take() blocks interruptibly when empty
+     */
+    public void testTakeFromEmptyBlocksInterruptibly() {
+        final BlockingQueue q = emptyCollection();
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                threadStarted.countDown();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(threadStarted);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * take() throws InterruptedException immediately if interrupted
+     * before waiting
+     */
+    public void testTakeFromEmptyAfterInterrupt() {
+        final BlockingQueue q = emptyCollection();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                Thread.currentThread().interrupt();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * timed poll() blocks interruptibly when empty
+     */
+    public void testTimedPollFromEmptyBlocksInterruptibly() {
+        final BlockingQueue q = emptyCollection();
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                threadStarted.countDown();
+                try {
+                    q.poll(2 * LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(threadStarted);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * timed poll() throws InterruptedException immediately if
+     * interrupted before waiting
+     */
+    public void testTimedPollFromEmptyAfterInterrupt() {
+        final BlockingQueue q = emptyCollection();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                Thread.currentThread().interrupt();
+                try {
+                    q.poll(2 * LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     * TODO: move to superclass CollectionTest.java
+     */
+    public void testRemoveElement() {
+        final BlockingQueue q = emptyCollection();
+        final int size = Math.min(q.remainingCapacity(), SIZE);
+        final Object[] elts = new Object[size];
+        assertFalse(q.contains(makeElement(99)));
+        assertFalse(q.remove(makeElement(99)));
+        checkEmpty(q);
+        for (int i = 0; i < size; i++)
+            q.add(elts[i] = makeElement(i));
+        for (int i = 1; i < size; i += 2) {
+            for (int pass = 0; pass < 2; pass++) {
+                assertEquals((pass == 0), q.contains(elts[i]));
+                assertEquals((pass == 0), q.remove(elts[i]));
+                assertFalse(q.contains(elts[i]));
+                assertTrue(q.contains(elts[i - 1]));
+                if (i < size - 1)
+                    assertTrue(q.contains(elts[i + 1]));
+            }
+        }
+        if (size > 0)
+            assertTrue(q.contains(elts[0]));
+        for (int i = size - 2; i >= 0; i -= 2) {
+            assertTrue(q.contains(elts[i]));
+            assertFalse(q.contains(elts[i + 1]));
+            assertTrue(q.remove(elts[i]));
+            assertFalse(q.contains(elts[i]));
+            assertFalse(q.remove(elts[i + 1]));
+            assertFalse(q.contains(elts[i + 1]));
+        }
+        checkEmpty(q);
+    }
+
+    /** For debugging. */
+    public void XXXXtestFails() {
+        fail(emptyCollection().getClass().toString());
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/Collection8Test.java b/jdk/test/java/util/concurrent/tck/Collection8Test.java
new file mode 100644
index 00000000000..1a10b280228
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/Collection8Test.java
@@ -0,0 +1,124 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea and Martin Buchholz with assistance from
+ * members of JCP JSR-166 Expert Group and released to the public
+ * domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Consumer;
+
+import junit.framework.Test;
+
+/**
+ * Contains tests applicable to all jdk8+ Collection implementations.
+ * An extension of CollectionTest.
+ */
+public class Collection8Test extends JSR166TestCase {
+    final CollectionImplementation impl;
+
+    /** Tests are parameterized by a Collection implementation. */
+    Collection8Test(CollectionImplementation impl, String methodName) {
+        super(methodName);
+        this.impl = impl;
+    }
+
+    public static Test testSuite(CollectionImplementation impl) {
+        return parameterizedTestSuite(Collection8Test.class,
+                                      CollectionImplementation.class,
+                                      impl);
+    }
+
+    /**
+     * stream().forEach returns elements in the collection
+     */
+    public void testForEach() throws Throwable {
+        final Collection c = impl.emptyCollection();
+        final AtomicLong count = new AtomicLong(0L);
+        final Object x = impl.makeElement(1);
+        final Object y = impl.makeElement(2);
+        final ArrayList found = new ArrayList();
+        Consumer<Object> spy = (o) -> { found.add(o); };
+        c.stream().forEach(spy);
+        assertTrue(found.isEmpty());
+
+        assertTrue(c.add(x));
+        c.stream().forEach(spy);
+        assertEquals(Collections.singletonList(x), found);
+        found.clear();
+
+        assertTrue(c.add(y));
+        c.stream().forEach(spy);
+        assertEquals(2, found.size());
+        assertTrue(found.contains(x));
+        assertTrue(found.contains(y));
+        found.clear();
+
+        c.clear();
+        c.stream().forEach(spy);
+        assertTrue(found.isEmpty());
+    }
+
+    public void testForEachConcurrentStressTest() throws Throwable {
+        if (!impl.isConcurrent()) return;
+        final Collection c = impl.emptyCollection();
+        final long testDurationMillis = SHORT_DELAY_MS;
+        final AtomicBoolean done = new AtomicBoolean(false);
+        final Object elt = impl.makeElement(1);
+        ExecutorService pool = Executors.newCachedThreadPool();
+        Runnable checkElt = () -> {
+            while (!done.get())
+                c.stream().forEach((x) -> { assertSame(x, elt); }); };
+        Runnable addRemove = () -> {
+            while (!done.get()) {
+                assertTrue(c.add(elt));
+                assertTrue(c.remove(elt));
+            }};
+        Future<?> f1 = pool.submit(checkElt);
+        Future<?> f2 = pool.submit(addRemove);
+        Thread.sleep(testDurationMillis);
+        done.set(true);
+        pool.shutdown();
+        assertTrue(pool.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+        assertNull(f1.get(LONG_DELAY_MS, MILLISECONDS));
+        assertNull(f2.get(LONG_DELAY_MS, MILLISECONDS));
+    }
+
+    // public void testCollection8DebugFail() { fail(); }
+}
diff --git a/jdk/test/java/util/concurrent/tck/CollectionImplementation.java b/jdk/test/java/util/concurrent/tck/CollectionImplementation.java
new file mode 100644
index 00000000000..54ffb7c0921
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/CollectionImplementation.java
@@ -0,0 +1,46 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea and Martin Buchholz with assistance from
+ * members of JCP JSR-166 Expert Group and released to the public
+ * domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import java.util.Collection;
+
+/** Allows tests to work with different Collection implementations. */
+public interface CollectionImplementation {
+    /** Returns the Collection class. */
+    public Class<?> klazz();
+    /** Returns an empty collection. */
+    public Collection emptyCollection();
+    public Object makeElement(int i);
+    public boolean isConcurrent();
+    public boolean permitsNulls();
+}
diff --git a/jdk/test/java/util/concurrent/tck/CollectionTest.java b/jdk/test/java/util/concurrent/tck/CollectionTest.java
new file mode 100644
index 00000000000..7b532af8019
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/CollectionTest.java
@@ -0,0 +1,68 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea and Martin Buchholz with assistance from
+ * members of JCP JSR-166 Expert Group and released to the public
+ * domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import java.util.Collection;
+
+import junit.framework.Test;
+
+/**
+ * Contains tests applicable to all Collection implementations.
+ */
+public class CollectionTest extends JSR166TestCase {
+    final CollectionImplementation impl;
+
+    /** Tests are parameterized by a Collection implementation. */
+    CollectionTest(CollectionImplementation impl, String methodName) {
+        super(methodName);
+        this.impl = impl;
+    }
+
+    public static Test testSuite(CollectionImplementation impl) {
+        return newTestSuite
+            (parameterizedTestSuite(CollectionTest.class,
+                                    CollectionImplementation.class,
+                                    impl),
+             jdk8ParameterizedTestSuite(CollectionTest.class,
+                                        CollectionImplementation.class,
+                                        impl));
+    }
+
+    /** A test of the CollectionImplementation implementation ! */
+    public void testEmptyMeansEmpty() {
+        assertTrue(impl.emptyCollection().isEmpty());
+        assertEquals(0, impl.emptyCollection().size());
+    }
+
+    // public void testCollectionDebugFail() { fail(); }
+}
diff --git a/jdk/test/java/util/concurrent/tck/CompletableFutureTest.java b/jdk/test/java/util/concurrent/tck/CompletableFutureTest.java
new file mode 100644
index 00000000000..a74a0403f2f
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/CompletableFutureTest.java
@@ -0,0 +1,3980 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea and Martin Buchholz with assistance from
+ * members of JCP JSR-166 Expert Group and released to the public
+ * domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static java.util.concurrent.CompletableFuture.completedFuture;
+import static java.util.concurrent.CompletableFuture.failedFuture;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class CompletableFutureTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(CompletableFutureTest.class);
+    }
+
+    static class CFException extends RuntimeException {}
+
+    void checkIncomplete(CompletableFuture<?> f) {
+        assertFalse(f.isDone());
+        assertFalse(f.isCancelled());
+        assertTrue(f.toString().contains("Not completed"));
+        try {
+            assertNull(f.getNow(null));
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        try {
+            f.get(0L, SECONDS);
+            shouldThrow();
+        }
+        catch (TimeoutException success) {}
+        catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    <T> void checkCompletedNormally(CompletableFuture<T> f, T value) {
+        checkTimedGet(f, value);
+
+        try {
+            assertEquals(value, f.join());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        try {
+            assertEquals(value, f.getNow(null));
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        try {
+            assertEquals(value, f.get());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        assertTrue(f.isDone());
+        assertFalse(f.isCancelled());
+        assertFalse(f.isCompletedExceptionally());
+        assertTrue(f.toString().contains("[Completed normally]"));
+    }
+
+    /**
+     * Returns the "raw" internal exceptional completion of f,
+     * without any additional wrapping with CompletionException.
+     */
+    <U> Throwable exceptionalCompletion(CompletableFuture<U> f) {
+        // handle (and whenComplete) can distinguish between "direct"
+        // and "wrapped" exceptional completion
+        return f.handle((U u, Throwable t) -> t).join();
+    }
+
+    void checkCompletedExceptionally(CompletableFuture<?> f,
+                                     boolean wrapped,
+                                     Consumer<Throwable> checker) {
+        Throwable cause = exceptionalCompletion(f);
+        if (wrapped) {
+            assertTrue(cause instanceof CompletionException);
+            cause = cause.getCause();
+        }
+        checker.accept(cause);
+
+        long startTime = System.nanoTime();
+        try {
+            f.get(LONG_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(cause, success.getCause());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS / 2);
+
+        try {
+            f.join();
+            shouldThrow();
+        } catch (CompletionException success) {
+            assertSame(cause, success.getCause());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            f.getNow(null);
+            shouldThrow();
+        } catch (CompletionException success) {
+            assertSame(cause, success.getCause());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            f.get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(cause, success.getCause());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        assertFalse(f.isCancelled());
+        assertTrue(f.isDone());
+        assertTrue(f.isCompletedExceptionally());
+        assertTrue(f.toString().contains("[Completed exceptionally]"));
+    }
+
+    void checkCompletedWithWrappedCFException(CompletableFuture<?> f) {
+        checkCompletedExceptionally(f, true,
+            (t) -> assertTrue(t instanceof CFException));
+    }
+
+    void checkCompletedWithWrappedCancellationException(CompletableFuture<?> f) {
+        checkCompletedExceptionally(f, true,
+            (t) -> assertTrue(t instanceof CancellationException));
+    }
+
+    void checkCompletedWithTimeoutException(CompletableFuture<?> f) {
+        checkCompletedExceptionally(f, false,
+            (t) -> assertTrue(t instanceof TimeoutException));
+    }
+
+    void checkCompletedWithWrappedException(CompletableFuture<?> f,
+                                            Throwable ex) {
+        checkCompletedExceptionally(f, true, (t) -> assertSame(t, ex));
+    }
+
+    void checkCompletedExceptionally(CompletableFuture<?> f, Throwable ex) {
+        checkCompletedExceptionally(f, false, (t) -> assertSame(t, ex));
+    }
+
+    void checkCancelled(CompletableFuture<?> f) {
+        long startTime = System.nanoTime();
+        try {
+            f.get(LONG_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS / 2);
+
+        try {
+            f.join();
+            shouldThrow();
+        } catch (CancellationException success) {}
+        try {
+            f.getNow(null);
+            shouldThrow();
+        } catch (CancellationException success) {}
+        try {
+            f.get();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        assertTrue(exceptionalCompletion(f) instanceof CancellationException);
+
+        assertTrue(f.isDone());
+        assertTrue(f.isCompletedExceptionally());
+        assertTrue(f.isCancelled());
+        assertTrue(f.toString().contains("[Completed exceptionally]"));
+    }
+
+    /**
+     * A newly constructed CompletableFuture is incomplete, as indicated
+     * by methods isDone, isCancelled, and getNow
+     */
+    public void testConstructor() {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        checkIncomplete(f);
+    }
+
+    /**
+     * complete completes normally, as indicated by methods isDone,
+     * isCancelled, join, get, and getNow
+     */
+    public void testComplete() {
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        checkIncomplete(f);
+        assertTrue(f.complete(v1));
+        assertFalse(f.complete(v1));
+        checkCompletedNormally(f, v1);
+    }}
+
+    /**
+     * completeExceptionally completes exceptionally, as indicated by
+     * methods isDone, isCancelled, join, get, and getNow
+     */
+    public void testCompleteExceptionally() {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        CFException ex = new CFException();
+        checkIncomplete(f);
+        f.completeExceptionally(ex);
+        checkCompletedExceptionally(f, ex);
+    }
+
+    /**
+     * cancel completes exceptionally and reports cancelled, as indicated by
+     * methods isDone, isCancelled, join, get, and getNow
+     */
+    public void testCancel() {
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+    {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        checkIncomplete(f);
+        assertTrue(f.cancel(mayInterruptIfRunning));
+        assertTrue(f.cancel(mayInterruptIfRunning));
+        assertTrue(f.cancel(!mayInterruptIfRunning));
+        checkCancelled(f);
+    }}
+
+    /**
+     * obtrudeValue forces completion with given value
+     */
+    public void testObtrudeValue() {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        checkIncomplete(f);
+        assertTrue(f.complete(one));
+        checkCompletedNormally(f, one);
+        f.obtrudeValue(three);
+        checkCompletedNormally(f, three);
+        f.obtrudeValue(two);
+        checkCompletedNormally(f, two);
+        f = new CompletableFuture<>();
+        f.obtrudeValue(three);
+        checkCompletedNormally(f, three);
+        f.obtrudeValue(null);
+        checkCompletedNormally(f, null);
+        f = new CompletableFuture<>();
+        f.completeExceptionally(new CFException());
+        f.obtrudeValue(four);
+        checkCompletedNormally(f, four);
+    }
+
+    /**
+     * obtrudeException forces completion with given exception
+     */
+    public void testObtrudeException() {
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        CFException ex;
+        CompletableFuture<Integer> f;
+
+        f = new CompletableFuture<>();
+        assertTrue(f.complete(v1));
+        for (int i = 0; i < 2; i++) {
+            f.obtrudeException(ex = new CFException());
+            checkCompletedExceptionally(f, ex);
+        }
+
+        f = new CompletableFuture<>();
+        for (int i = 0; i < 2; i++) {
+            f.obtrudeException(ex = new CFException());
+            checkCompletedExceptionally(f, ex);
+        }
+
+        f = new CompletableFuture<>();
+        f.completeExceptionally(ex = new CFException());
+        f.obtrudeValue(v1);
+        checkCompletedNormally(f, v1);
+        f.obtrudeException(ex = new CFException());
+        checkCompletedExceptionally(f, ex);
+        f.completeExceptionally(new CFException());
+        checkCompletedExceptionally(f, ex);
+        assertFalse(f.complete(v1));
+        checkCompletedExceptionally(f, ex);
+    }}
+
+    /**
+     * getNumberOfDependents returns number of dependent tasks
+     */
+    public void testGetNumberOfDependents() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        assertEquals(0, f.getNumberOfDependents());
+        final CompletableFuture<Void> g = m.thenRun(f, new Noop(m));
+        assertEquals(1, f.getNumberOfDependents());
+        assertEquals(0, g.getNumberOfDependents());
+        final CompletableFuture<Void> h = m.thenRun(f, new Noop(m));
+        assertEquals(2, f.getNumberOfDependents());
+        assertEquals(0, h.getNumberOfDependents());
+        assertTrue(f.complete(v1));
+        checkCompletedNormally(g, null);
+        checkCompletedNormally(h, null);
+        assertEquals(0, f.getNumberOfDependents());
+        assertEquals(0, g.getNumberOfDependents());
+        assertEquals(0, h.getNumberOfDependents());
+    }}
+
+    /**
+     * toString indicates current completion state
+     */
+    public void testToString() {
+        CompletableFuture<String> f;
+
+        f = new CompletableFuture<String>();
+        assertTrue(f.toString().contains("[Not completed]"));
+
+        assertTrue(f.complete("foo"));
+        assertTrue(f.toString().contains("[Completed normally]"));
+
+        f = new CompletableFuture<String>();
+        assertTrue(f.completeExceptionally(new IndexOutOfBoundsException()));
+        assertTrue(f.toString().contains("[Completed exceptionally]"));
+
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false }) {
+            f = new CompletableFuture<String>();
+            assertTrue(f.cancel(mayInterruptIfRunning));
+            assertTrue(f.toString().contains("[Completed exceptionally]"));
+        }
+    }
+
+    /**
+     * completedFuture returns a completed CompletableFuture with given value
+     */
+    public void testCompletedFuture() {
+        CompletableFuture<String> f = CompletableFuture.completedFuture("test");
+        checkCompletedNormally(f, "test");
+    }
+
+    abstract class CheckedAction {
+        int invocationCount = 0;
+        final ExecutionMode m;
+        CheckedAction(ExecutionMode m) { this.m = m; }
+        void invoked() {
+            m.checkExecutionMode();
+            assertEquals(0, invocationCount++);
+        }
+        void assertNotInvoked() { assertEquals(0, invocationCount); }
+        void assertInvoked() { assertEquals(1, invocationCount); }
+    }
+
+    abstract class CheckedIntegerAction extends CheckedAction {
+        Integer value;
+        CheckedIntegerAction(ExecutionMode m) { super(m); }
+        void assertValue(Integer expected) {
+            assertInvoked();
+            assertEquals(expected, value);
+        }
+    }
+
+    class IntegerSupplier extends CheckedAction
+        implements Supplier<Integer>
+    {
+        final Integer value;
+        IntegerSupplier(ExecutionMode m, Integer value) {
+            super(m);
+            this.value = value;
+        }
+        public Integer get() {
+            invoked();
+            return value;
+        }
+    }
+
+    // A function that handles and produces null values as well.
+    static Integer inc(Integer x) {
+        return (x == null) ? null : x + 1;
+    }
+
+    class NoopConsumer extends CheckedIntegerAction
+        implements Consumer<Integer>
+    {
+        NoopConsumer(ExecutionMode m) { super(m); }
+        public void accept(Integer x) {
+            invoked();
+            value = x;
+        }
+    }
+
+    class IncFunction extends CheckedIntegerAction
+        implements Function<Integer,Integer>
+    {
+        IncFunction(ExecutionMode m) { super(m); }
+        public Integer apply(Integer x) {
+            invoked();
+            return value = inc(x);
+        }
+    }
+
+    // Choose non-commutative actions for better coverage
+    // A non-commutative function that handles and produces null values as well.
+    static Integer subtract(Integer x, Integer y) {
+        return (x == null && y == null) ? null :
+            ((x == null) ? 42 : x.intValue())
+            - ((y == null) ? 99 : y.intValue());
+    }
+
+    class SubtractAction extends CheckedIntegerAction
+        implements BiConsumer<Integer, Integer>
+    {
+        SubtractAction(ExecutionMode m) { super(m); }
+        public void accept(Integer x, Integer y) {
+            invoked();
+            value = subtract(x, y);
+        }
+    }
+
+    class SubtractFunction extends CheckedIntegerAction
+        implements BiFunction<Integer, Integer, Integer>
+    {
+        SubtractFunction(ExecutionMode m) { super(m); }
+        public Integer apply(Integer x, Integer y) {
+            invoked();
+            return value = subtract(x, y);
+        }
+    }
+
+    class Noop extends CheckedAction implements Runnable {
+        Noop(ExecutionMode m) { super(m); }
+        public void run() {
+            invoked();
+        }
+    }
+
+    class FailingSupplier extends CheckedAction
+        implements Supplier<Integer>
+    {
+        FailingSupplier(ExecutionMode m) { super(m); }
+        public Integer get() {
+            invoked();
+            throw new CFException();
+        }
+    }
+
+    class FailingConsumer extends CheckedIntegerAction
+        implements Consumer<Integer>
+    {
+        FailingConsumer(ExecutionMode m) { super(m); }
+        public void accept(Integer x) {
+            invoked();
+            value = x;
+            throw new CFException();
+        }
+    }
+
+    class FailingBiConsumer extends CheckedIntegerAction
+        implements BiConsumer<Integer, Integer>
+    {
+        FailingBiConsumer(ExecutionMode m) { super(m); }
+        public void accept(Integer x, Integer y) {
+            invoked();
+            value = subtract(x, y);
+            throw new CFException();
+        }
+    }
+
+    class FailingFunction extends CheckedIntegerAction
+        implements Function<Integer, Integer>
+    {
+        FailingFunction(ExecutionMode m) { super(m); }
+        public Integer apply(Integer x) {
+            invoked();
+            value = x;
+            throw new CFException();
+        }
+    }
+
+    class FailingBiFunction extends CheckedIntegerAction
+        implements BiFunction<Integer, Integer, Integer>
+    {
+        FailingBiFunction(ExecutionMode m) { super(m); }
+        public Integer apply(Integer x, Integer y) {
+            invoked();
+            value = subtract(x, y);
+            throw new CFException();
+        }
+    }
+
+    class FailingRunnable extends CheckedAction implements Runnable {
+        FailingRunnable(ExecutionMode m) { super(m); }
+        public void run() {
+            invoked();
+            throw new CFException();
+        }
+    }
+
+    class CompletableFutureInc extends CheckedIntegerAction
+        implements Function<Integer, CompletableFuture<Integer>>
+    {
+        CompletableFutureInc(ExecutionMode m) { super(m); }
+        public CompletableFuture<Integer> apply(Integer x) {
+            invoked();
+            value = x;
+            CompletableFuture<Integer> f = new CompletableFuture<>();
+            assertTrue(f.complete(inc(x)));
+            return f;
+        }
+    }
+
+    class FailingCompletableFutureFunction extends CheckedIntegerAction
+        implements Function<Integer, CompletableFuture<Integer>>
+    {
+        FailingCompletableFutureFunction(ExecutionMode m) { super(m); }
+        public CompletableFuture<Integer> apply(Integer x) {
+            invoked();
+            value = x;
+            throw new CFException();
+        }
+    }
+
+    // Used for explicit executor tests
+    static final class ThreadExecutor implements Executor {
+        final AtomicInteger count = new AtomicInteger(0);
+        static final ThreadGroup tg = new ThreadGroup("ThreadExecutor");
+        static boolean startedCurrentThread() {
+            return Thread.currentThread().getThreadGroup() == tg;
+        }
+
+        public void execute(Runnable r) {
+            count.getAndIncrement();
+            new Thread(tg, r).start();
+        }
+    }
+
+    static final boolean defaultExecutorIsCommonPool
+        = ForkJoinPool.getCommonPoolParallelism() > 1;
+
+    /**
+     * Permits the testing of parallel code for the 3 different
+     * execution modes without copy/pasting all the test methods.
+     */
+    enum ExecutionMode {
+        SYNC {
+            public void checkExecutionMode() {
+                assertFalse(ThreadExecutor.startedCurrentThread());
+                assertNull(ForkJoinTask.getPool());
+            }
+            public CompletableFuture<Void> runAsync(Runnable a) {
+                throw new UnsupportedOperationException();
+            }
+            public <U> CompletableFuture<U> supplyAsync(Supplier<U> a) {
+                throw new UnsupportedOperationException();
+            }
+            public <T> CompletableFuture<Void> thenRun
+                (CompletableFuture<T> f, Runnable a) {
+                return f.thenRun(a);
+            }
+            public <T> CompletableFuture<Void> thenAccept
+                (CompletableFuture<T> f, Consumer<? super T> a) {
+                return f.thenAccept(a);
+            }
+            public <T,U> CompletableFuture<U> thenApply
+                (CompletableFuture<T> f, Function<? super T,U> a) {
+                return f.thenApply(a);
+            }
+            public <T,U> CompletableFuture<U> thenCompose
+                (CompletableFuture<T> f,
+                 Function<? super T,? extends CompletionStage<U>> a) {
+                return f.thenCompose(a);
+            }
+            public <T,U> CompletableFuture<U> handle
+                (CompletableFuture<T> f,
+                 BiFunction<? super T,Throwable,? extends U> a) {
+                return f.handle(a);
+            }
+            public <T> CompletableFuture<T> whenComplete
+                (CompletableFuture<T> f,
+                 BiConsumer<? super T,? super Throwable> a) {
+                return f.whenComplete(a);
+            }
+            public <T,U> CompletableFuture<Void> runAfterBoth
+                (CompletableFuture<T> f, CompletableFuture<U> g, Runnable a) {
+                return f.runAfterBoth(g, a);
+            }
+            public <T,U> CompletableFuture<Void> thenAcceptBoth
+                (CompletableFuture<T> f,
+                 CompletionStage<? extends U> g,
+                 BiConsumer<? super T,? super U> a) {
+                return f.thenAcceptBoth(g, a);
+            }
+            public <T,U,V> CompletableFuture<V> thenCombine
+                (CompletableFuture<T> f,
+                 CompletionStage<? extends U> g,
+                 BiFunction<? super T,? super U,? extends V> a) {
+                return f.thenCombine(g, a);
+            }
+            public <T> CompletableFuture<Void> runAfterEither
+                (CompletableFuture<T> f,
+                 CompletionStage<?> g,
+                 java.lang.Runnable a) {
+                return f.runAfterEither(g, a);
+            }
+            public <T> CompletableFuture<Void> acceptEither
+                (CompletableFuture<T> f,
+                 CompletionStage<? extends T> g,
+                 Consumer<? super T> a) {
+                return f.acceptEither(g, a);
+            }
+            public <T,U> CompletableFuture<U> applyToEither
+                (CompletableFuture<T> f,
+                 CompletionStage<? extends T> g,
+                 Function<? super T,U> a) {
+                return f.applyToEither(g, a);
+            }
+        },
+
+        ASYNC {
+            public void checkExecutionMode() {
+                assertEquals(defaultExecutorIsCommonPool,
+                             (ForkJoinPool.commonPool() == ForkJoinTask.getPool()));
+            }
+            public CompletableFuture<Void> runAsync(Runnable a) {
+                return CompletableFuture.runAsync(a);
+            }
+            public <U> CompletableFuture<U> supplyAsync(Supplier<U> a) {
+                return CompletableFuture.supplyAsync(a);
+            }
+            public <T> CompletableFuture<Void> thenRun
+                (CompletableFuture<T> f, Runnable a) {
+                return f.thenRunAsync(a);
+            }
+            public <T> CompletableFuture<Void> thenAccept
+                (CompletableFuture<T> f, Consumer<? super T> a) {
+                return f.thenAcceptAsync(a);
+            }
+            public <T,U> CompletableFuture<U> thenApply
+                (CompletableFuture<T> f, Function<? super T,U> a) {
+                return f.thenApplyAsync(a);
+            }
+            public <T,U> CompletableFuture<U> thenCompose
+                (CompletableFuture<T> f,
+                 Function<? super T,? extends CompletionStage<U>> a) {
+                return f.thenComposeAsync(a);
+            }
+            public <T,U> CompletableFuture<U> handle
+                (CompletableFuture<T> f,
+                 BiFunction<? super T,Throwable,? extends U> a) {
+                return f.handleAsync(a);
+            }
+            public <T> CompletableFuture<T> whenComplete
+                (CompletableFuture<T> f,
+                 BiConsumer<? super T,? super Throwable> a) {
+                return f.whenCompleteAsync(a);
+            }
+            public <T,U> CompletableFuture<Void> runAfterBoth
+                (CompletableFuture<T> f, CompletableFuture<U> g, Runnable a) {
+                return f.runAfterBothAsync(g, a);
+            }
+            public <T,U> CompletableFuture<Void> thenAcceptBoth
+                (CompletableFuture<T> f,
+                 CompletionStage<? extends U> g,
+                 BiConsumer<? super T,? super U> a) {
+                return f.thenAcceptBothAsync(g, a);
+            }
+            public <T,U,V> CompletableFuture<V> thenCombine
+                (CompletableFuture<T> f,
+                 CompletionStage<? extends U> g,
+                 BiFunction<? super T,? super U,? extends V> a) {
+                return f.thenCombineAsync(g, a);
+            }
+            public <T> CompletableFuture<Void> runAfterEither
+                (CompletableFuture<T> f,
+                 CompletionStage<?> g,
+                 java.lang.Runnable a) {
+                return f.runAfterEitherAsync(g, a);
+            }
+            public <T> CompletableFuture<Void> acceptEither
+                (CompletableFuture<T> f,
+                 CompletionStage<? extends T> g,
+                 Consumer<? super T> a) {
+                return f.acceptEitherAsync(g, a);
+            }
+            public <T,U> CompletableFuture<U> applyToEither
+                (CompletableFuture<T> f,
+                 CompletionStage<? extends T> g,
+                 Function<? super T,U> a) {
+                return f.applyToEitherAsync(g, a);
+            }
+        },
+
+        EXECUTOR {
+            public void checkExecutionMode() {
+                assertTrue(ThreadExecutor.startedCurrentThread());
+            }
+            public CompletableFuture<Void> runAsync(Runnable a) {
+                return CompletableFuture.runAsync(a, new ThreadExecutor());
+            }
+            public <U> CompletableFuture<U> supplyAsync(Supplier<U> a) {
+                return CompletableFuture.supplyAsync(a, new ThreadExecutor());
+            }
+            public <T> CompletableFuture<Void> thenRun
+                (CompletableFuture<T> f, Runnable a) {
+                return f.thenRunAsync(a, new ThreadExecutor());
+            }
+            public <T> CompletableFuture<Void> thenAccept
+                (CompletableFuture<T> f, Consumer<? super T> a) {
+                return f.thenAcceptAsync(a, new ThreadExecutor());
+            }
+            public <T,U> CompletableFuture<U> thenApply
+                (CompletableFuture<T> f, Function<? super T,U> a) {
+                return f.thenApplyAsync(a, new ThreadExecutor());
+            }
+            public <T,U> CompletableFuture<U> thenCompose
+                (CompletableFuture<T> f,
+                 Function<? super T,? extends CompletionStage<U>> a) {
+                return f.thenComposeAsync(a, new ThreadExecutor());
+            }
+            public <T,U> CompletableFuture<U> handle
+                (CompletableFuture<T> f,
+                 BiFunction<? super T,Throwable,? extends U> a) {
+                return f.handleAsync(a, new ThreadExecutor());
+            }
+            public <T> CompletableFuture<T> whenComplete
+                (CompletableFuture<T> f,
+                 BiConsumer<? super T,? super Throwable> a) {
+                return f.whenCompleteAsync(a, new ThreadExecutor());
+            }
+            public <T,U> CompletableFuture<Void> runAfterBoth
+                (CompletableFuture<T> f, CompletableFuture<U> g, Runnable a) {
+                return f.runAfterBothAsync(g, a, new ThreadExecutor());
+            }
+            public <T,U> CompletableFuture<Void> thenAcceptBoth
+                (CompletableFuture<T> f,
+                 CompletionStage<? extends U> g,
+                 BiConsumer<? super T,? super U> a) {
+                return f.thenAcceptBothAsync(g, a, new ThreadExecutor());
+            }
+            public <T,U,V> CompletableFuture<V> thenCombine
+                (CompletableFuture<T> f,
+                 CompletionStage<? extends U> g,
+                 BiFunction<? super T,? super U,? extends V> a) {
+                return f.thenCombineAsync(g, a, new ThreadExecutor());
+            }
+            public <T> CompletableFuture<Void> runAfterEither
+                (CompletableFuture<T> f,
+                 CompletionStage<?> g,
+                 java.lang.Runnable a) {
+                return f.runAfterEitherAsync(g, a, new ThreadExecutor());
+            }
+            public <T> CompletableFuture<Void> acceptEither
+                (CompletableFuture<T> f,
+                 CompletionStage<? extends T> g,
+                 Consumer<? super T> a) {
+                return f.acceptEitherAsync(g, a, new ThreadExecutor());
+            }
+            public <T,U> CompletableFuture<U> applyToEither
+                (CompletableFuture<T> f,
+                 CompletionStage<? extends T> g,
+                 Function<? super T,U> a) {
+                return f.applyToEitherAsync(g, a, new ThreadExecutor());
+            }
+        };
+
+        public abstract void checkExecutionMode();
+        public abstract CompletableFuture<Void> runAsync(Runnable a);
+        public abstract <U> CompletableFuture<U> supplyAsync(Supplier<U> a);
+        public abstract <T> CompletableFuture<Void> thenRun
+            (CompletableFuture<T> f, Runnable a);
+        public abstract <T> CompletableFuture<Void> thenAccept
+            (CompletableFuture<T> f, Consumer<? super T> a);
+        public abstract <T,U> CompletableFuture<U> thenApply
+            (CompletableFuture<T> f, Function<? super T,U> a);
+        public abstract <T,U> CompletableFuture<U> thenCompose
+            (CompletableFuture<T> f,
+             Function<? super T,? extends CompletionStage<U>> a);
+        public abstract <T,U> CompletableFuture<U> handle
+            (CompletableFuture<T> f,
+             BiFunction<? super T,Throwable,? extends U> a);
+        public abstract <T> CompletableFuture<T> whenComplete
+            (CompletableFuture<T> f,
+             BiConsumer<? super T,? super Throwable> a);
+        public abstract <T,U> CompletableFuture<Void> runAfterBoth
+            (CompletableFuture<T> f, CompletableFuture<U> g, Runnable a);
+        public abstract <T,U> CompletableFuture<Void> thenAcceptBoth
+            (CompletableFuture<T> f,
+             CompletionStage<? extends U> g,
+             BiConsumer<? super T,? super U> a);
+        public abstract <T,U,V> CompletableFuture<V> thenCombine
+            (CompletableFuture<T> f,
+             CompletionStage<? extends U> g,
+             BiFunction<? super T,? super U,? extends V> a);
+        public abstract <T> CompletableFuture<Void> runAfterEither
+            (CompletableFuture<T> f,
+             CompletionStage<?> g,
+             java.lang.Runnable a);
+        public abstract <T> CompletableFuture<Void> acceptEither
+            (CompletableFuture<T> f,
+             CompletionStage<? extends T> g,
+             Consumer<? super T> a);
+        public abstract <T,U> CompletableFuture<U> applyToEither
+            (CompletableFuture<T> f,
+             CompletionStage<? extends T> g,
+             Function<? super T,U> a);
+    }
+
+    /**
+     * exceptionally action is not invoked when source completes
+     * normally, and source result is propagated
+     */
+    public void testExceptionally_normalCompletion() {
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final AtomicInteger a = new AtomicInteger(0);
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        if (!createIncomplete) assertTrue(f.complete(v1));
+        final CompletableFuture<Integer> g = f.exceptionally
+            ((Throwable t) -> {
+                a.getAndIncrement();
+                threadFail("should not be called");
+                return null;            // unreached
+            });
+        if (createIncomplete) assertTrue(f.complete(v1));
+
+        checkCompletedNormally(g, v1);
+        checkCompletedNormally(f, v1);
+        assertEquals(0, a.get());
+    }}
+
+    /**
+     * exceptionally action completes with function value on source
+     * exception
+     */
+    public void testExceptionally_exceptionalCompletion() {
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final AtomicInteger a = new AtomicInteger(0);
+        final CFException ex = new CFException();
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        if (!createIncomplete) f.completeExceptionally(ex);
+        final CompletableFuture<Integer> g = f.exceptionally
+            ((Throwable t) -> {
+                ExecutionMode.SYNC.checkExecutionMode();
+                threadAssertSame(t, ex);
+                a.getAndIncrement();
+                return v1;
+            });
+        if (createIncomplete) f.completeExceptionally(ex);
+
+        checkCompletedNormally(g, v1);
+        assertEquals(1, a.get());
+    }}
+
+    /**
+     * If an "exceptionally action" throws an exception, it completes
+     * exceptionally with that exception
+     */
+    public void testExceptionally_exceptionalCompletionActionFailed() {
+        for (boolean createIncomplete : new boolean[] { true, false })
+    {
+        final AtomicInteger a = new AtomicInteger(0);
+        final CFException ex1 = new CFException();
+        final CFException ex2 = new CFException();
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        if (!createIncomplete) f.completeExceptionally(ex1);
+        final CompletableFuture<Integer> g = f.exceptionally
+            ((Throwable t) -> {
+                ExecutionMode.SYNC.checkExecutionMode();
+                threadAssertSame(t, ex1);
+                a.getAndIncrement();
+                throw ex2;
+            });
+        if (createIncomplete) f.completeExceptionally(ex1);
+
+        checkCompletedWithWrappedException(g, ex2);
+        checkCompletedExceptionally(f, ex1);
+        assertEquals(1, a.get());
+    }}
+
+    /**
+     * whenComplete action executes on normal completion, propagating
+     * source result.
+     */
+    public void testWhenComplete_normalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final AtomicInteger a = new AtomicInteger(0);
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        if (!createIncomplete) assertTrue(f.complete(v1));
+        final CompletableFuture<Integer> g = m.whenComplete
+            (f,
+             (Integer result, Throwable t) -> {
+                m.checkExecutionMode();
+                threadAssertSame(result, v1);
+                threadAssertNull(t);
+                a.getAndIncrement();
+            });
+        if (createIncomplete) assertTrue(f.complete(v1));
+
+        checkCompletedNormally(g, v1);
+        checkCompletedNormally(f, v1);
+        assertEquals(1, a.get());
+    }}
+
+    /**
+     * whenComplete action executes on exceptional completion, propagating
+     * source result.
+     */
+    public void testWhenComplete_exceptionalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean createIncomplete : new boolean[] { true, false })
+    {
+        final AtomicInteger a = new AtomicInteger(0);
+        final CFException ex = new CFException();
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        if (!createIncomplete) f.completeExceptionally(ex);
+        final CompletableFuture<Integer> g = m.whenComplete
+            (f,
+             (Integer result, Throwable t) -> {
+                m.checkExecutionMode();
+                threadAssertNull(result);
+                threadAssertSame(t, ex);
+                a.getAndIncrement();
+            });
+        if (createIncomplete) f.completeExceptionally(ex);
+
+        checkCompletedWithWrappedException(g, ex);
+        checkCompletedExceptionally(f, ex);
+        assertEquals(1, a.get());
+    }}
+
+    /**
+     * whenComplete action executes on cancelled source, propagating
+     * CancellationException.
+     */
+    public void testWhenComplete_sourceCancelled() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+        for (boolean createIncomplete : new boolean[] { true, false })
+    {
+        final AtomicInteger a = new AtomicInteger(0);
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        if (!createIncomplete) assertTrue(f.cancel(mayInterruptIfRunning));
+        final CompletableFuture<Integer> g = m.whenComplete
+            (f,
+             (Integer result, Throwable t) -> {
+                m.checkExecutionMode();
+                threadAssertNull(result);
+                threadAssertTrue(t instanceof CancellationException);
+                a.getAndIncrement();
+            });
+        if (createIncomplete) assertTrue(f.cancel(mayInterruptIfRunning));
+
+        checkCompletedWithWrappedCancellationException(g);
+        checkCancelled(f);
+        assertEquals(1, a.get());
+    }}
+
+    /**
+     * If a whenComplete action throws an exception when triggered by
+     * a normal completion, it completes exceptionally
+     */
+    public void testWhenComplete_sourceCompletedNormallyActionFailed() {
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final AtomicInteger a = new AtomicInteger(0);
+        final CFException ex = new CFException();
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        if (!createIncomplete) assertTrue(f.complete(v1));
+        final CompletableFuture<Integer> g = m.whenComplete
+            (f,
+             (Integer result, Throwable t) -> {
+                m.checkExecutionMode();
+                threadAssertSame(result, v1);
+                threadAssertNull(t);
+                a.getAndIncrement();
+                throw ex;
+            });
+        if (createIncomplete) assertTrue(f.complete(v1));
+
+        checkCompletedWithWrappedException(g, ex);
+        checkCompletedNormally(f, v1);
+        assertEquals(1, a.get());
+    }}
+
+    /**
+     * If a whenComplete action throws an exception when triggered by
+     * a source completion that also throws an exception, the source
+     * exception takes precedence (unlike handle)
+     */
+    public void testWhenComplete_sourceFailedActionFailed() {
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (ExecutionMode m : ExecutionMode.values())
+    {
+        final AtomicInteger a = new AtomicInteger(0);
+        final CFException ex1 = new CFException();
+        final CFException ex2 = new CFException();
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+
+        if (!createIncomplete) f.completeExceptionally(ex1);
+        final CompletableFuture<Integer> g = m.whenComplete
+            (f,
+             (Integer result, Throwable t) -> {
+                m.checkExecutionMode();
+                threadAssertSame(t, ex1);
+                threadAssertNull(result);
+                a.getAndIncrement();
+                throw ex2;
+            });
+        if (createIncomplete) f.completeExceptionally(ex1);
+
+        checkCompletedWithWrappedException(g, ex1);
+        checkCompletedExceptionally(f, ex1);
+        // oops... temporarily disabled
+//         if (testImplementationDetails) {
+//             assertEquals(1, ex1.getSuppressed().length);
+//             assertSame(ex2, ex1.getSuppressed()[0]);
+//         }
+        assertEquals(1, a.get());
+    }}
+
+    /**
+     * handle action completes normally with function value on normal
+     * completion of source
+     */
+    public void testHandle_normalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final AtomicInteger a = new AtomicInteger(0);
+        if (!createIncomplete) assertTrue(f.complete(v1));
+        final CompletableFuture<Integer> g = m.handle
+            (f,
+             (Integer result, Throwable t) -> {
+                m.checkExecutionMode();
+                threadAssertSame(result, v1);
+                threadAssertNull(t);
+                a.getAndIncrement();
+                return inc(v1);
+            });
+        if (createIncomplete) assertTrue(f.complete(v1));
+
+        checkCompletedNormally(g, inc(v1));
+        checkCompletedNormally(f, v1);
+        assertEquals(1, a.get());
+    }}
+
+    /**
+     * handle action completes normally with function value on
+     * exceptional completion of source
+     */
+    public void testHandle_exceptionalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final AtomicInteger a = new AtomicInteger(0);
+        final CFException ex = new CFException();
+        if (!createIncomplete) f.completeExceptionally(ex);
+        final CompletableFuture<Integer> g = m.handle
+            (f,
+             (Integer result, Throwable t) -> {
+                m.checkExecutionMode();
+                threadAssertNull(result);
+                threadAssertSame(t, ex);
+                a.getAndIncrement();
+                return v1;
+            });
+        if (createIncomplete) f.completeExceptionally(ex);
+
+        checkCompletedNormally(g, v1);
+        checkCompletedExceptionally(f, ex);
+        assertEquals(1, a.get());
+    }}
+
+    /**
+     * handle action completes normally with function value on
+     * cancelled source
+     */
+    public void testHandle_sourceCancelled() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final AtomicInteger a = new AtomicInteger(0);
+        if (!createIncomplete) assertTrue(f.cancel(mayInterruptIfRunning));
+        final CompletableFuture<Integer> g = m.handle
+            (f,
+             (Integer result, Throwable t) -> {
+                m.checkExecutionMode();
+                threadAssertNull(result);
+                threadAssertTrue(t instanceof CancellationException);
+                a.getAndIncrement();
+                return v1;
+            });
+        if (createIncomplete) assertTrue(f.cancel(mayInterruptIfRunning));
+
+        checkCompletedNormally(g, v1);
+        checkCancelled(f);
+        assertEquals(1, a.get());
+    }}
+
+    /**
+     * If a "handle action" throws an exception when triggered by
+     * a normal completion, it completes exceptionally
+     */
+    public void testHandle_sourceCompletedNormallyActionFailed() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final AtomicInteger a = new AtomicInteger(0);
+        final CFException ex = new CFException();
+        if (!createIncomplete) assertTrue(f.complete(v1));
+        final CompletableFuture<Integer> g = m.handle
+            (f,
+             (Integer result, Throwable t) -> {
+                m.checkExecutionMode();
+                threadAssertSame(result, v1);
+                threadAssertNull(t);
+                a.getAndIncrement();
+                throw ex;
+            });
+        if (createIncomplete) assertTrue(f.complete(v1));
+
+        checkCompletedWithWrappedException(g, ex);
+        checkCompletedNormally(f, v1);
+        assertEquals(1, a.get());
+    }}
+
+    /**
+     * If a "handle action" throws an exception when triggered by
+     * a source completion that also throws an exception, the action
+     * exception takes precedence (unlike whenComplete)
+     */
+    public void testHandle_sourceFailedActionFailed() {
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (ExecutionMode m : ExecutionMode.values())
+    {
+        final AtomicInteger a = new AtomicInteger(0);
+        final CFException ex1 = new CFException();
+        final CFException ex2 = new CFException();
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+
+        if (!createIncomplete) f.completeExceptionally(ex1);
+        final CompletableFuture<Integer> g = m.handle
+            (f,
+             (Integer result, Throwable t) -> {
+                m.checkExecutionMode();
+                threadAssertNull(result);
+                threadAssertSame(ex1, t);
+                a.getAndIncrement();
+                throw ex2;
+            });
+        if (createIncomplete) f.completeExceptionally(ex1);
+
+        checkCompletedWithWrappedException(g, ex2);
+        checkCompletedExceptionally(f, ex1);
+        assertEquals(1, a.get());
+    }}
+
+    /**
+     * runAsync completes after running Runnable
+     */
+    public void testRunAsync_normalCompletion() {
+        ExecutionMode[] executionModes = {
+            ExecutionMode.ASYNC,
+            ExecutionMode.EXECUTOR,
+        };
+        for (ExecutionMode m : executionModes)
+    {
+        final Noop r = new Noop(m);
+        final CompletableFuture<Void> f = m.runAsync(r);
+        assertNull(f.join());
+        checkCompletedNormally(f, null);
+        r.assertInvoked();
+    }}
+
+    /**
+     * failing runAsync completes exceptionally after running Runnable
+     */
+    public void testRunAsync_exceptionalCompletion() {
+        ExecutionMode[] executionModes = {
+            ExecutionMode.ASYNC,
+            ExecutionMode.EXECUTOR,
+        };
+        for (ExecutionMode m : executionModes)
+    {
+        final FailingRunnable r = new FailingRunnable(m);
+        final CompletableFuture<Void> f = m.runAsync(r);
+        checkCompletedWithWrappedCFException(f);
+        r.assertInvoked();
+    }}
+
+    /**
+     * supplyAsync completes with result of supplier
+     */
+    public void testSupplyAsync_normalCompletion() {
+        ExecutionMode[] executionModes = {
+            ExecutionMode.ASYNC,
+            ExecutionMode.EXECUTOR,
+        };
+        for (ExecutionMode m : executionModes)
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final IntegerSupplier r = new IntegerSupplier(m, v1);
+        final CompletableFuture<Integer> f = m.supplyAsync(r);
+        assertSame(v1, f.join());
+        checkCompletedNormally(f, v1);
+        r.assertInvoked();
+    }}
+
+    /**
+     * Failing supplyAsync completes exceptionally
+     */
+    public void testSupplyAsync_exceptionalCompletion() {
+        ExecutionMode[] executionModes = {
+            ExecutionMode.ASYNC,
+            ExecutionMode.EXECUTOR,
+        };
+        for (ExecutionMode m : executionModes)
+    {
+        FailingSupplier r = new FailingSupplier(m);
+        CompletableFuture<Integer> f = m.supplyAsync(r);
+        checkCompletedWithWrappedCFException(f);
+        r.assertInvoked();
+    }}
+
+    // seq completion methods
+
+    /**
+     * thenRun result completes normally after normal completion of source
+     */
+    public void testThenRun_normalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final Noop[] rs = new Noop[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m);
+
+        final CompletableFuture<Void> h0 = m.thenRun(f, rs[0]);
+        final CompletableFuture<Void> h1 = m.runAfterBoth(f, f, rs[1]);
+        final CompletableFuture<Void> h2 = m.runAfterEither(f, f, rs[2]);
+        checkIncomplete(h0);
+        checkIncomplete(h1);
+        checkIncomplete(h2);
+        assertTrue(f.complete(v1));
+        final CompletableFuture<Void> h3 = m.thenRun(f, rs[3]);
+        final CompletableFuture<Void> h4 = m.runAfterBoth(f, f, rs[4]);
+        final CompletableFuture<Void> h5 = m.runAfterEither(f, f, rs[5]);
+
+        checkCompletedNormally(h0, null);
+        checkCompletedNormally(h1, null);
+        checkCompletedNormally(h2, null);
+        checkCompletedNormally(h3, null);
+        checkCompletedNormally(h4, null);
+        checkCompletedNormally(h5, null);
+        checkCompletedNormally(f, v1);
+        for (Noop r : rs) r.assertInvoked();
+    }}
+
+    /**
+     * thenRun result completes exceptionally after exceptional
+     * completion of source
+     */
+    public void testThenRun_exceptionalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+    {
+        final CFException ex = new CFException();
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final Noop[] rs = new Noop[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m);
+
+        final CompletableFuture<Void> h0 = m.thenRun(f, rs[0]);
+        final CompletableFuture<Void> h1 = m.runAfterBoth(f, f, rs[1]);
+        final CompletableFuture<Void> h2 = m.runAfterEither(f, f, rs[2]);
+        checkIncomplete(h0);
+        checkIncomplete(h1);
+        checkIncomplete(h2);
+        assertTrue(f.completeExceptionally(ex));
+        final CompletableFuture<Void> h3 = m.thenRun(f, rs[3]);
+        final CompletableFuture<Void> h4 = m.runAfterBoth(f, f, rs[4]);
+        final CompletableFuture<Void> h5 = m.runAfterEither(f, f, rs[5]);
+
+        checkCompletedWithWrappedException(h0, ex);
+        checkCompletedWithWrappedException(h1, ex);
+        checkCompletedWithWrappedException(h2, ex);
+        checkCompletedWithWrappedException(h3, ex);
+        checkCompletedWithWrappedException(h4, ex);
+        checkCompletedWithWrappedException(h5, ex);
+        checkCompletedExceptionally(f, ex);
+        for (Noop r : rs) r.assertNotInvoked();
+    }}
+
+    /**
+     * thenRun result completes exceptionally if source cancelled
+     */
+    public void testThenRun_sourceCancelled() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final Noop[] rs = new Noop[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m);
+
+        final CompletableFuture<Void> h0 = m.thenRun(f, rs[0]);
+        final CompletableFuture<Void> h1 = m.runAfterBoth(f, f, rs[1]);
+        final CompletableFuture<Void> h2 = m.runAfterEither(f, f, rs[2]);
+        checkIncomplete(h0);
+        checkIncomplete(h1);
+        checkIncomplete(h2);
+        assertTrue(f.cancel(mayInterruptIfRunning));
+        final CompletableFuture<Void> h3 = m.thenRun(f, rs[3]);
+        final CompletableFuture<Void> h4 = m.runAfterBoth(f, f, rs[4]);
+        final CompletableFuture<Void> h5 = m.runAfterEither(f, f, rs[5]);
+
+        checkCompletedWithWrappedCancellationException(h0);
+        checkCompletedWithWrappedCancellationException(h1);
+        checkCompletedWithWrappedCancellationException(h2);
+        checkCompletedWithWrappedCancellationException(h3);
+        checkCompletedWithWrappedCancellationException(h4);
+        checkCompletedWithWrappedCancellationException(h5);
+        checkCancelled(f);
+        for (Noop r : rs) r.assertNotInvoked();
+    }}
+
+    /**
+     * thenRun result completes exceptionally if action does
+     */
+    public void testThenRun_actionFailed() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final FailingRunnable[] rs = new FailingRunnable[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new FailingRunnable(m);
+
+        final CompletableFuture<Void> h0 = m.thenRun(f, rs[0]);
+        final CompletableFuture<Void> h1 = m.runAfterBoth(f, f, rs[1]);
+        final CompletableFuture<Void> h2 = m.runAfterEither(f, f, rs[2]);
+        assertTrue(f.complete(v1));
+        final CompletableFuture<Void> h3 = m.thenRun(f, rs[3]);
+        final CompletableFuture<Void> h4 = m.runAfterBoth(f, f, rs[4]);
+        final CompletableFuture<Void> h5 = m.runAfterEither(f, f, rs[5]);
+
+        checkCompletedWithWrappedCFException(h0);
+        checkCompletedWithWrappedCFException(h1);
+        checkCompletedWithWrappedCFException(h2);
+        checkCompletedWithWrappedCFException(h3);
+        checkCompletedWithWrappedCFException(h4);
+        checkCompletedWithWrappedCFException(h5);
+        checkCompletedNormally(f, v1);
+    }}
+
+    /**
+     * thenApply result completes normally after normal completion of source
+     */
+    public void testThenApply_normalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final IncFunction[] rs = new IncFunction[4];
+        for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m);
+
+        final CompletableFuture<Integer> h0 = m.thenApply(f, rs[0]);
+        final CompletableFuture<Integer> h1 = m.applyToEither(f, f, rs[1]);
+        checkIncomplete(h0);
+        checkIncomplete(h1);
+        assertTrue(f.complete(v1));
+        final CompletableFuture<Integer> h2 = m.thenApply(f, rs[2]);
+        final CompletableFuture<Integer> h3 = m.applyToEither(f, f, rs[3]);
+
+        checkCompletedNormally(h0, inc(v1));
+        checkCompletedNormally(h1, inc(v1));
+        checkCompletedNormally(h2, inc(v1));
+        checkCompletedNormally(h3, inc(v1));
+        checkCompletedNormally(f, v1);
+        for (IncFunction r : rs) r.assertValue(inc(v1));
+    }}
+
+    /**
+     * thenApply result completes exceptionally after exceptional
+     * completion of source
+     */
+    public void testThenApply_exceptionalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+    {
+        final CFException ex = new CFException();
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final IncFunction[] rs = new IncFunction[4];
+        for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m);
+
+        final CompletableFuture<Integer> h0 = m.thenApply(f, rs[0]);
+        final CompletableFuture<Integer> h1 = m.applyToEither(f, f, rs[1]);
+        assertTrue(f.completeExceptionally(ex));
+        final CompletableFuture<Integer> h2 = m.thenApply(f, rs[2]);
+        final CompletableFuture<Integer> h3 = m.applyToEither(f, f, rs[3]);
+
+        checkCompletedWithWrappedException(h0, ex);
+        checkCompletedWithWrappedException(h1, ex);
+        checkCompletedWithWrappedException(h2, ex);
+        checkCompletedWithWrappedException(h3, ex);
+        checkCompletedExceptionally(f, ex);
+        for (IncFunction r : rs) r.assertNotInvoked();
+    }}
+
+    /**
+     * thenApply result completes exceptionally if source cancelled
+     */
+    public void testThenApply_sourceCancelled() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final IncFunction[] rs = new IncFunction[4];
+        for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m);
+
+        final CompletableFuture<Integer> h0 = m.thenApply(f, rs[0]);
+        final CompletableFuture<Integer> h1 = m.applyToEither(f, f, rs[1]);
+        assertTrue(f.cancel(mayInterruptIfRunning));
+        final CompletableFuture<Integer> h2 = m.thenApply(f, rs[2]);
+        final CompletableFuture<Integer> h3 = m.applyToEither(f, f, rs[3]);
+
+        checkCompletedWithWrappedCancellationException(h0);
+        checkCompletedWithWrappedCancellationException(h1);
+        checkCompletedWithWrappedCancellationException(h2);
+        checkCompletedWithWrappedCancellationException(h3);
+        checkCancelled(f);
+        for (IncFunction r : rs) r.assertNotInvoked();
+    }}
+
+    /**
+     * thenApply result completes exceptionally if action does
+     */
+    public void testThenApply_actionFailed() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final FailingFunction[] rs = new FailingFunction[4];
+        for (int i = 0; i < rs.length; i++) rs[i] = new FailingFunction(m);
+
+        final CompletableFuture<Integer> h0 = m.thenApply(f, rs[0]);
+        final CompletableFuture<Integer> h1 = m.applyToEither(f, f, rs[1]);
+        assertTrue(f.complete(v1));
+        final CompletableFuture<Integer> h2 = m.thenApply(f, rs[2]);
+        final CompletableFuture<Integer> h3 = m.applyToEither(f, f, rs[3]);
+
+        checkCompletedWithWrappedCFException(h0);
+        checkCompletedWithWrappedCFException(h1);
+        checkCompletedWithWrappedCFException(h2);
+        checkCompletedWithWrappedCFException(h3);
+        checkCompletedNormally(f, v1);
+    }}
+
+    /**
+     * thenAccept result completes normally after normal completion of source
+     */
+    public void testThenAccept_normalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final NoopConsumer[] rs = new NoopConsumer[4];
+        for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m);
+
+        final CompletableFuture<Void> h0 = m.thenAccept(f, rs[0]);
+        final CompletableFuture<Void> h1 = m.acceptEither(f, f, rs[1]);
+        checkIncomplete(h0);
+        checkIncomplete(h1);
+        assertTrue(f.complete(v1));
+        final CompletableFuture<Void> h2 = m.thenAccept(f, rs[2]);
+        final CompletableFuture<Void> h3 = m.acceptEither(f, f, rs[3]);
+
+        checkCompletedNormally(h0, null);
+        checkCompletedNormally(h1, null);
+        checkCompletedNormally(h2, null);
+        checkCompletedNormally(h3, null);
+        checkCompletedNormally(f, v1);
+        for (NoopConsumer r : rs) r.assertValue(v1);
+    }}
+
+    /**
+     * thenAccept result completes exceptionally after exceptional
+     * completion of source
+     */
+    public void testThenAccept_exceptionalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+    {
+        final CFException ex = new CFException();
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final NoopConsumer[] rs = new NoopConsumer[4];
+        for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m);
+
+        final CompletableFuture<Void> h0 = m.thenAccept(f, rs[0]);
+        final CompletableFuture<Void> h1 = m.acceptEither(f, f, rs[1]);
+        assertTrue(f.completeExceptionally(ex));
+        final CompletableFuture<Void> h2 = m.thenAccept(f, rs[2]);
+        final CompletableFuture<Void> h3 = m.acceptEither(f, f, rs[3]);
+
+        checkCompletedWithWrappedException(h0, ex);
+        checkCompletedWithWrappedException(h1, ex);
+        checkCompletedWithWrappedException(h2, ex);
+        checkCompletedWithWrappedException(h3, ex);
+        checkCompletedExceptionally(f, ex);
+        for (NoopConsumer r : rs) r.assertNotInvoked();
+    }}
+
+    /**
+     * thenAccept result completes exceptionally if source cancelled
+     */
+    public void testThenAccept_sourceCancelled() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final NoopConsumer[] rs = new NoopConsumer[4];
+        for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m);
+
+        final CompletableFuture<Void> h0 = m.thenAccept(f, rs[0]);
+        final CompletableFuture<Void> h1 = m.acceptEither(f, f, rs[1]);
+        assertTrue(f.cancel(mayInterruptIfRunning));
+        final CompletableFuture<Void> h2 = m.thenAccept(f, rs[2]);
+        final CompletableFuture<Void> h3 = m.acceptEither(f, f, rs[3]);
+
+        checkCompletedWithWrappedCancellationException(h0);
+        checkCompletedWithWrappedCancellationException(h1);
+        checkCompletedWithWrappedCancellationException(h2);
+        checkCompletedWithWrappedCancellationException(h3);
+        checkCancelled(f);
+        for (NoopConsumer r : rs) r.assertNotInvoked();
+    }}
+
+    /**
+     * thenAccept result completes exceptionally if action does
+     */
+    public void testThenAccept_actionFailed() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final FailingConsumer[] rs = new FailingConsumer[4];
+        for (int i = 0; i < rs.length; i++) rs[i] = new FailingConsumer(m);
+
+        final CompletableFuture<Void> h0 = m.thenAccept(f, rs[0]);
+        final CompletableFuture<Void> h1 = m.acceptEither(f, f, rs[1]);
+        assertTrue(f.complete(v1));
+        final CompletableFuture<Void> h2 = m.thenAccept(f, rs[2]);
+        final CompletableFuture<Void> h3 = m.acceptEither(f, f, rs[3]);
+
+        checkCompletedWithWrappedCFException(h0);
+        checkCompletedWithWrappedCFException(h1);
+        checkCompletedWithWrappedCFException(h2);
+        checkCompletedWithWrappedCFException(h3);
+        checkCompletedNormally(f, v1);
+    }}
+
+    /**
+     * thenCombine result completes normally after normal completion
+     * of sources
+     */
+    public void testThenCombine_normalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean fFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+        for (Integer v2 : new Integer[] { 2, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final SubtractFunction[] rs = new SubtractFunction[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new SubtractFunction(m);
+
+        final CompletableFuture<Integer> fst =  fFirst ? f : g;
+        final CompletableFuture<Integer> snd = !fFirst ? f : g;
+        final Integer w1 =  fFirst ? v1 : v2;
+        final Integer w2 = !fFirst ? v1 : v2;
+
+        final CompletableFuture<Integer> h0 = m.thenCombine(f, g, rs[0]);
+        final CompletableFuture<Integer> h1 = m.thenCombine(fst, fst, rs[1]);
+        assertTrue(fst.complete(w1));
+        final CompletableFuture<Integer> h2 = m.thenCombine(f, g, rs[2]);
+        final CompletableFuture<Integer> h3 = m.thenCombine(fst, fst, rs[3]);
+        checkIncomplete(h0); rs[0].assertNotInvoked();
+        checkIncomplete(h2); rs[2].assertNotInvoked();
+        checkCompletedNormally(h1, subtract(w1, w1));
+        checkCompletedNormally(h3, subtract(w1, w1));
+        rs[1].assertValue(subtract(w1, w1));
+        rs[3].assertValue(subtract(w1, w1));
+        assertTrue(snd.complete(w2));
+        final CompletableFuture<Integer> h4 = m.thenCombine(f, g, rs[4]);
+
+        checkCompletedNormally(h0, subtract(v1, v2));
+        checkCompletedNormally(h2, subtract(v1, v2));
+        checkCompletedNormally(h4, subtract(v1, v2));
+        rs[0].assertValue(subtract(v1, v2));
+        rs[2].assertValue(subtract(v1, v2));
+        rs[4].assertValue(subtract(v1, v2));
+
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v2);
+    }}
+
+    /**
+     * thenCombine result completes exceptionally after exceptional
+     * completion of either source
+     */
+    public void testThenCombine_exceptionalCompletion() throws Throwable {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean fFirst : new boolean[] { true, false })
+        for (boolean failFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final CFException ex = new CFException();
+        final SubtractFunction r1 = new SubtractFunction(m);
+        final SubtractFunction r2 = new SubtractFunction(m);
+        final SubtractFunction r3 = new SubtractFunction(m);
+
+        final CompletableFuture<Integer> fst =  fFirst ? f : g;
+        final CompletableFuture<Integer> snd = !fFirst ? f : g;
+        final Callable<Boolean> complete1 = failFirst ?
+            () -> fst.completeExceptionally(ex) :
+            () -> fst.complete(v1);
+        final Callable<Boolean> complete2 = failFirst ?
+            () -> snd.complete(v1) :
+            () -> snd.completeExceptionally(ex);
+
+        final CompletableFuture<Integer> h1 = m.thenCombine(f, g, r1);
+        assertTrue(complete1.call());
+        final CompletableFuture<Integer> h2 = m.thenCombine(f, g, r2);
+        checkIncomplete(h1);
+        checkIncomplete(h2);
+        assertTrue(complete2.call());
+        final CompletableFuture<Integer> h3 = m.thenCombine(f, g, r3);
+
+        checkCompletedWithWrappedException(h1, ex);
+        checkCompletedWithWrappedException(h2, ex);
+        checkCompletedWithWrappedException(h3, ex);
+        r1.assertNotInvoked();
+        r2.assertNotInvoked();
+        r3.assertNotInvoked();
+        checkCompletedNormally(failFirst ? snd : fst, v1);
+        checkCompletedExceptionally(failFirst ? fst : snd, ex);
+    }}
+
+    /**
+     * thenCombine result completes exceptionally if either source cancelled
+     */
+    public void testThenCombine_sourceCancelled() throws Throwable {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+        for (boolean fFirst : new boolean[] { true, false })
+        for (boolean failFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final SubtractFunction r1 = new SubtractFunction(m);
+        final SubtractFunction r2 = new SubtractFunction(m);
+        final SubtractFunction r3 = new SubtractFunction(m);
+
+        final CompletableFuture<Integer> fst =  fFirst ? f : g;
+        final CompletableFuture<Integer> snd = !fFirst ? f : g;
+        final Callable<Boolean> complete1 = failFirst ?
+            () -> fst.cancel(mayInterruptIfRunning) :
+            () -> fst.complete(v1);
+        final Callable<Boolean> complete2 = failFirst ?
+            () -> snd.complete(v1) :
+            () -> snd.cancel(mayInterruptIfRunning);
+
+        final CompletableFuture<Integer> h1 = m.thenCombine(f, g, r1);
+        assertTrue(complete1.call());
+        final CompletableFuture<Integer> h2 = m.thenCombine(f, g, r2);
+        checkIncomplete(h1);
+        checkIncomplete(h2);
+        assertTrue(complete2.call());
+        final CompletableFuture<Integer> h3 = m.thenCombine(f, g, r3);
+
+        checkCompletedWithWrappedCancellationException(h1);
+        checkCompletedWithWrappedCancellationException(h2);
+        checkCompletedWithWrappedCancellationException(h3);
+        r1.assertNotInvoked();
+        r2.assertNotInvoked();
+        r3.assertNotInvoked();
+        checkCompletedNormally(failFirst ? snd : fst, v1);
+        checkCancelled(failFirst ? fst : snd);
+    }}
+
+    /**
+     * thenCombine result completes exceptionally if action does
+     */
+    public void testThenCombine_actionFailed() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean fFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+        for (Integer v2 : new Integer[] { 2, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final FailingBiFunction r1 = new FailingBiFunction(m);
+        final FailingBiFunction r2 = new FailingBiFunction(m);
+        final FailingBiFunction r3 = new FailingBiFunction(m);
+
+        final CompletableFuture<Integer> fst =  fFirst ? f : g;
+        final CompletableFuture<Integer> snd = !fFirst ? f : g;
+        final Integer w1 =  fFirst ? v1 : v2;
+        final Integer w2 = !fFirst ? v1 : v2;
+
+        final CompletableFuture<Integer> h1 = m.thenCombine(f, g, r1);
+        assertTrue(fst.complete(w1));
+        final CompletableFuture<Integer> h2 = m.thenCombine(f, g, r2);
+        assertTrue(snd.complete(w2));
+        final CompletableFuture<Integer> h3 = m.thenCombine(f, g, r3);
+
+        checkCompletedWithWrappedCFException(h1);
+        checkCompletedWithWrappedCFException(h2);
+        checkCompletedWithWrappedCFException(h3);
+        r1.assertInvoked();
+        r2.assertInvoked();
+        r3.assertInvoked();
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v2);
+    }}
+
+    /**
+     * thenAcceptBoth result completes normally after normal
+     * completion of sources
+     */
+    public void testThenAcceptBoth_normalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean fFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+        for (Integer v2 : new Integer[] { 2, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final SubtractAction r1 = new SubtractAction(m);
+        final SubtractAction r2 = new SubtractAction(m);
+        final SubtractAction r3 = new SubtractAction(m);
+
+        final CompletableFuture<Integer> fst =  fFirst ? f : g;
+        final CompletableFuture<Integer> snd = !fFirst ? f : g;
+        final Integer w1 =  fFirst ? v1 : v2;
+        final Integer w2 = !fFirst ? v1 : v2;
+
+        final CompletableFuture<Void> h1 = m.thenAcceptBoth(f, g, r1);
+        assertTrue(fst.complete(w1));
+        final CompletableFuture<Void> h2 = m.thenAcceptBoth(f, g, r2);
+        checkIncomplete(h1);
+        checkIncomplete(h2);
+        r1.assertNotInvoked();
+        r2.assertNotInvoked();
+        assertTrue(snd.complete(w2));
+        final CompletableFuture<Void> h3 = m.thenAcceptBoth(f, g, r3);
+
+        checkCompletedNormally(h1, null);
+        checkCompletedNormally(h2, null);
+        checkCompletedNormally(h3, null);
+        r1.assertValue(subtract(v1, v2));
+        r2.assertValue(subtract(v1, v2));
+        r3.assertValue(subtract(v1, v2));
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v2);
+    }}
+
+    /**
+     * thenAcceptBoth result completes exceptionally after exceptional
+     * completion of either source
+     */
+    public void testThenAcceptBoth_exceptionalCompletion() throws Throwable {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean fFirst : new boolean[] { true, false })
+        for (boolean failFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final CFException ex = new CFException();
+        final SubtractAction r1 = new SubtractAction(m);
+        final SubtractAction r2 = new SubtractAction(m);
+        final SubtractAction r3 = new SubtractAction(m);
+
+        final CompletableFuture<Integer> fst =  fFirst ? f : g;
+        final CompletableFuture<Integer> snd = !fFirst ? f : g;
+        final Callable<Boolean> complete1 = failFirst ?
+            () -> fst.completeExceptionally(ex) :
+            () -> fst.complete(v1);
+        final Callable<Boolean> complete2 = failFirst ?
+            () -> snd.complete(v1) :
+            () -> snd.completeExceptionally(ex);
+
+        final CompletableFuture<Void> h1 = m.thenAcceptBoth(f, g, r1);
+        assertTrue(complete1.call());
+        final CompletableFuture<Void> h2 = m.thenAcceptBoth(f, g, r2);
+        checkIncomplete(h1);
+        checkIncomplete(h2);
+        assertTrue(complete2.call());
+        final CompletableFuture<Void> h3 = m.thenAcceptBoth(f, g, r3);
+
+        checkCompletedWithWrappedException(h1, ex);
+        checkCompletedWithWrappedException(h2, ex);
+        checkCompletedWithWrappedException(h3, ex);
+        r1.assertNotInvoked();
+        r2.assertNotInvoked();
+        r3.assertNotInvoked();
+        checkCompletedNormally(failFirst ? snd : fst, v1);
+        checkCompletedExceptionally(failFirst ? fst : snd, ex);
+    }}
+
+    /**
+     * thenAcceptBoth result completes exceptionally if either source cancelled
+     */
+    public void testThenAcceptBoth_sourceCancelled() throws Throwable {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+        for (boolean fFirst : new boolean[] { true, false })
+        for (boolean failFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final SubtractAction r1 = new SubtractAction(m);
+        final SubtractAction r2 = new SubtractAction(m);
+        final SubtractAction r3 = new SubtractAction(m);
+
+        final CompletableFuture<Integer> fst =  fFirst ? f : g;
+        final CompletableFuture<Integer> snd = !fFirst ? f : g;
+        final Callable<Boolean> complete1 = failFirst ?
+            () -> fst.cancel(mayInterruptIfRunning) :
+            () -> fst.complete(v1);
+        final Callable<Boolean> complete2 = failFirst ?
+            () -> snd.complete(v1) :
+            () -> snd.cancel(mayInterruptIfRunning);
+
+        final CompletableFuture<Void> h1 = m.thenAcceptBoth(f, g, r1);
+        assertTrue(complete1.call());
+        final CompletableFuture<Void> h2 = m.thenAcceptBoth(f, g, r2);
+        checkIncomplete(h1);
+        checkIncomplete(h2);
+        assertTrue(complete2.call());
+        final CompletableFuture<Void> h3 = m.thenAcceptBoth(f, g, r3);
+
+        checkCompletedWithWrappedCancellationException(h1);
+        checkCompletedWithWrappedCancellationException(h2);
+        checkCompletedWithWrappedCancellationException(h3);
+        r1.assertNotInvoked();
+        r2.assertNotInvoked();
+        r3.assertNotInvoked();
+        checkCompletedNormally(failFirst ? snd : fst, v1);
+        checkCancelled(failFirst ? fst : snd);
+    }}
+
+    /**
+     * thenAcceptBoth result completes exceptionally if action does
+     */
+    public void testThenAcceptBoth_actionFailed() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean fFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+        for (Integer v2 : new Integer[] { 2, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final FailingBiConsumer r1 = new FailingBiConsumer(m);
+        final FailingBiConsumer r2 = new FailingBiConsumer(m);
+        final FailingBiConsumer r3 = new FailingBiConsumer(m);
+
+        final CompletableFuture<Integer> fst =  fFirst ? f : g;
+        final CompletableFuture<Integer> snd = !fFirst ? f : g;
+        final Integer w1 =  fFirst ? v1 : v2;
+        final Integer w2 = !fFirst ? v1 : v2;
+
+        final CompletableFuture<Void> h1 = m.thenAcceptBoth(f, g, r1);
+        assertTrue(fst.complete(w1));
+        final CompletableFuture<Void> h2 = m.thenAcceptBoth(f, g, r2);
+        assertTrue(snd.complete(w2));
+        final CompletableFuture<Void> h3 = m.thenAcceptBoth(f, g, r3);
+
+        checkCompletedWithWrappedCFException(h1);
+        checkCompletedWithWrappedCFException(h2);
+        checkCompletedWithWrappedCFException(h3);
+        r1.assertInvoked();
+        r2.assertInvoked();
+        r3.assertInvoked();
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v2);
+    }}
+
+    /**
+     * runAfterBoth result completes normally after normal
+     * completion of sources
+     */
+    public void testRunAfterBoth_normalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean fFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+        for (Integer v2 : new Integer[] { 2, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final Noop r1 = new Noop(m);
+        final Noop r2 = new Noop(m);
+        final Noop r3 = new Noop(m);
+
+        final CompletableFuture<Integer> fst =  fFirst ? f : g;
+        final CompletableFuture<Integer> snd = !fFirst ? f : g;
+        final Integer w1 =  fFirst ? v1 : v2;
+        final Integer w2 = !fFirst ? v1 : v2;
+
+        final CompletableFuture<Void> h1 = m.runAfterBoth(f, g, r1);
+        assertTrue(fst.complete(w1));
+        final CompletableFuture<Void> h2 = m.runAfterBoth(f, g, r2);
+        checkIncomplete(h1);
+        checkIncomplete(h2);
+        r1.assertNotInvoked();
+        r2.assertNotInvoked();
+        assertTrue(snd.complete(w2));
+        final CompletableFuture<Void> h3 = m.runAfterBoth(f, g, r3);
+
+        checkCompletedNormally(h1, null);
+        checkCompletedNormally(h2, null);
+        checkCompletedNormally(h3, null);
+        r1.assertInvoked();
+        r2.assertInvoked();
+        r3.assertInvoked();
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v2);
+    }}
+
+    /**
+     * runAfterBoth result completes exceptionally after exceptional
+     * completion of either source
+     */
+    public void testRunAfterBoth_exceptionalCompletion() throws Throwable {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean fFirst : new boolean[] { true, false })
+        for (boolean failFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final CFException ex = new CFException();
+        final Noop r1 = new Noop(m);
+        final Noop r2 = new Noop(m);
+        final Noop r3 = new Noop(m);
+
+        final CompletableFuture<Integer> fst =  fFirst ? f : g;
+        final CompletableFuture<Integer> snd = !fFirst ? f : g;
+        final Callable<Boolean> complete1 = failFirst ?
+            () -> fst.completeExceptionally(ex) :
+            () -> fst.complete(v1);
+        final Callable<Boolean> complete2 = failFirst ?
+            () -> snd.complete(v1) :
+            () -> snd.completeExceptionally(ex);
+
+        final CompletableFuture<Void> h1 = m.runAfterBoth(f, g, r1);
+        assertTrue(complete1.call());
+        final CompletableFuture<Void> h2 = m.runAfterBoth(f, g, r2);
+        checkIncomplete(h1);
+        checkIncomplete(h2);
+        assertTrue(complete2.call());
+        final CompletableFuture<Void> h3 = m.runAfterBoth(f, g, r3);
+
+        checkCompletedWithWrappedException(h1, ex);
+        checkCompletedWithWrappedException(h2, ex);
+        checkCompletedWithWrappedException(h3, ex);
+        r1.assertNotInvoked();
+        r2.assertNotInvoked();
+        r3.assertNotInvoked();
+        checkCompletedNormally(failFirst ? snd : fst, v1);
+        checkCompletedExceptionally(failFirst ? fst : snd, ex);
+    }}
+
+    /**
+     * runAfterBoth result completes exceptionally if either source cancelled
+     */
+    public void testRunAfterBoth_sourceCancelled() throws Throwable {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+        for (boolean fFirst : new boolean[] { true, false })
+        for (boolean failFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final Noop r1 = new Noop(m);
+        final Noop r2 = new Noop(m);
+        final Noop r3 = new Noop(m);
+
+        final CompletableFuture<Integer> fst =  fFirst ? f : g;
+        final CompletableFuture<Integer> snd = !fFirst ? f : g;
+        final Callable<Boolean> complete1 = failFirst ?
+            () -> fst.cancel(mayInterruptIfRunning) :
+            () -> fst.complete(v1);
+        final Callable<Boolean> complete2 = failFirst ?
+            () -> snd.complete(v1) :
+            () -> snd.cancel(mayInterruptIfRunning);
+
+        final CompletableFuture<Void> h1 = m.runAfterBoth(f, g, r1);
+        assertTrue(complete1.call());
+        final CompletableFuture<Void> h2 = m.runAfterBoth(f, g, r2);
+        checkIncomplete(h1);
+        checkIncomplete(h2);
+        assertTrue(complete2.call());
+        final CompletableFuture<Void> h3 = m.runAfterBoth(f, g, r3);
+
+        checkCompletedWithWrappedCancellationException(h1);
+        checkCompletedWithWrappedCancellationException(h2);
+        checkCompletedWithWrappedCancellationException(h3);
+        r1.assertNotInvoked();
+        r2.assertNotInvoked();
+        r3.assertNotInvoked();
+        checkCompletedNormally(failFirst ? snd : fst, v1);
+        checkCancelled(failFirst ? fst : snd);
+    }}
+
+    /**
+     * runAfterBoth result completes exceptionally if action does
+     */
+    public void testRunAfterBoth_actionFailed() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean fFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+        for (Integer v2 : new Integer[] { 2, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final FailingRunnable r1 = new FailingRunnable(m);
+        final FailingRunnable r2 = new FailingRunnable(m);
+        final FailingRunnable r3 = new FailingRunnable(m);
+
+        final CompletableFuture<Integer> fst =  fFirst ? f : g;
+        final CompletableFuture<Integer> snd = !fFirst ? f : g;
+        final Integer w1 =  fFirst ? v1 : v2;
+        final Integer w2 = !fFirst ? v1 : v2;
+
+        final CompletableFuture<Void> h1 = m.runAfterBoth(f, g, r1);
+        assertTrue(fst.complete(w1));
+        final CompletableFuture<Void> h2 = m.runAfterBoth(f, g, r2);
+        assertTrue(snd.complete(w2));
+        final CompletableFuture<Void> h3 = m.runAfterBoth(f, g, r3);
+
+        checkCompletedWithWrappedCFException(h1);
+        checkCompletedWithWrappedCFException(h2);
+        checkCompletedWithWrappedCFException(h3);
+        r1.assertInvoked();
+        r2.assertInvoked();
+        r3.assertInvoked();
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v2);
+    }}
+
+    /**
+     * applyToEither result completes normally after normal completion
+     * of either source
+     */
+    public void testApplyToEither_normalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+        for (Integer v2 : new Integer[] { 2, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final IncFunction[] rs = new IncFunction[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m);
+
+        final CompletableFuture<Integer> h0 = m.applyToEither(f, g, rs[0]);
+        final CompletableFuture<Integer> h1 = m.applyToEither(g, f, rs[1]);
+        checkIncomplete(h0);
+        checkIncomplete(h1);
+        rs[0].assertNotInvoked();
+        rs[1].assertNotInvoked();
+        f.complete(v1);
+        checkCompletedNormally(h0, inc(v1));
+        checkCompletedNormally(h1, inc(v1));
+        final CompletableFuture<Integer> h2 = m.applyToEither(f, g, rs[2]);
+        final CompletableFuture<Integer> h3 = m.applyToEither(g, f, rs[3]);
+        checkCompletedNormally(h2, inc(v1));
+        checkCompletedNormally(h3, inc(v1));
+        g.complete(v2);
+
+        // unspecified behavior - both source completions available
+        final CompletableFuture<Integer> h4 = m.applyToEither(f, g, rs[4]);
+        final CompletableFuture<Integer> h5 = m.applyToEither(g, f, rs[5]);
+        rs[4].assertValue(h4.join());
+        rs[5].assertValue(h5.join());
+        assertTrue(Objects.equals(inc(v1), h4.join()) ||
+                   Objects.equals(inc(v2), h4.join()));
+        assertTrue(Objects.equals(inc(v1), h5.join()) ||
+                   Objects.equals(inc(v2), h5.join()));
+
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v2);
+        checkCompletedNormally(h0, inc(v1));
+        checkCompletedNormally(h1, inc(v1));
+        checkCompletedNormally(h2, inc(v1));
+        checkCompletedNormally(h3, inc(v1));
+        for (int i = 0; i < 4; i++) rs[i].assertValue(inc(v1));
+    }}
+
+    /**
+     * applyToEither result completes exceptionally after exceptional
+     * completion of either source
+     */
+    public void testApplyToEither_exceptionalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final CFException ex = new CFException();
+        final IncFunction[] rs = new IncFunction[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m);
+
+        final CompletableFuture<Integer> h0 = m.applyToEither(f, g, rs[0]);
+        final CompletableFuture<Integer> h1 = m.applyToEither(g, f, rs[1]);
+        checkIncomplete(h0);
+        checkIncomplete(h1);
+        rs[0].assertNotInvoked();
+        rs[1].assertNotInvoked();
+        f.completeExceptionally(ex);
+        checkCompletedWithWrappedException(h0, ex);
+        checkCompletedWithWrappedException(h1, ex);
+        final CompletableFuture<Integer> h2 = m.applyToEither(f, g, rs[2]);
+        final CompletableFuture<Integer> h3 = m.applyToEither(g, f, rs[3]);
+        checkCompletedWithWrappedException(h2, ex);
+        checkCompletedWithWrappedException(h3, ex);
+        g.complete(v1);
+
+        // unspecified behavior - both source completions available
+        final CompletableFuture<Integer> h4 = m.applyToEither(f, g, rs[4]);
+        final CompletableFuture<Integer> h5 = m.applyToEither(g, f, rs[5]);
+        try {
+            assertEquals(inc(v1), h4.join());
+            rs[4].assertValue(inc(v1));
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h4, ex);
+            rs[4].assertNotInvoked();
+        }
+        try {
+            assertEquals(inc(v1), h5.join());
+            rs[5].assertValue(inc(v1));
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h5, ex);
+            rs[5].assertNotInvoked();
+        }
+
+        checkCompletedExceptionally(f, ex);
+        checkCompletedNormally(g, v1);
+        checkCompletedWithWrappedException(h0, ex);
+        checkCompletedWithWrappedException(h1, ex);
+        checkCompletedWithWrappedException(h2, ex);
+        checkCompletedWithWrappedException(h3, ex);
+        checkCompletedWithWrappedException(h4, ex);
+        for (int i = 0; i < 4; i++) rs[i].assertNotInvoked();
+    }}
+
+    public void testApplyToEither_exceptionalCompletion2() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean fFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final CFException ex = new CFException();
+        final IncFunction[] rs = new IncFunction[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m);
+
+        final CompletableFuture<Integer> h0 = m.applyToEither(f, g, rs[0]);
+        final CompletableFuture<Integer> h1 = m.applyToEither(g, f, rs[1]);
+        assertTrue(fFirst ? f.complete(v1) : g.completeExceptionally(ex));
+        assertTrue(!fFirst ? f.complete(v1) : g.completeExceptionally(ex));
+        final CompletableFuture<Integer> h2 = m.applyToEither(f, g, rs[2]);
+        final CompletableFuture<Integer> h3 = m.applyToEither(g, f, rs[3]);
+
+        // unspecified behavior - both source completions available
+        try {
+            assertEquals(inc(v1), h0.join());
+            rs[0].assertValue(inc(v1));
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h0, ex);
+            rs[0].assertNotInvoked();
+        }
+        try {
+            assertEquals(inc(v1), h1.join());
+            rs[1].assertValue(inc(v1));
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h1, ex);
+            rs[1].assertNotInvoked();
+        }
+        try {
+            assertEquals(inc(v1), h2.join());
+            rs[2].assertValue(inc(v1));
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h2, ex);
+            rs[2].assertNotInvoked();
+        }
+        try {
+            assertEquals(inc(v1), h3.join());
+            rs[3].assertValue(inc(v1));
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h3, ex);
+            rs[3].assertNotInvoked();
+        }
+
+        checkCompletedNormally(f, v1);
+        checkCompletedExceptionally(g, ex);
+    }}
+
+    /**
+     * applyToEither result completes exceptionally if either source cancelled
+     */
+    public void testApplyToEither_sourceCancelled() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final IncFunction[] rs = new IncFunction[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m);
+
+        final CompletableFuture<Integer> h0 = m.applyToEither(f, g, rs[0]);
+        final CompletableFuture<Integer> h1 = m.applyToEither(g, f, rs[1]);
+        checkIncomplete(h0);
+        checkIncomplete(h1);
+        rs[0].assertNotInvoked();
+        rs[1].assertNotInvoked();
+        f.cancel(mayInterruptIfRunning);
+        checkCompletedWithWrappedCancellationException(h0);
+        checkCompletedWithWrappedCancellationException(h1);
+        final CompletableFuture<Integer> h2 = m.applyToEither(f, g, rs[2]);
+        final CompletableFuture<Integer> h3 = m.applyToEither(g, f, rs[3]);
+        checkCompletedWithWrappedCancellationException(h2);
+        checkCompletedWithWrappedCancellationException(h3);
+        g.complete(v1);
+
+        // unspecified behavior - both source completions available
+        final CompletableFuture<Integer> h4 = m.applyToEither(f, g, rs[4]);
+        final CompletableFuture<Integer> h5 = m.applyToEither(g, f, rs[5]);
+        try {
+            assertEquals(inc(v1), h4.join());
+            rs[4].assertValue(inc(v1));
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedCancellationException(h4);
+            rs[4].assertNotInvoked();
+        }
+        try {
+            assertEquals(inc(v1), h5.join());
+            rs[5].assertValue(inc(v1));
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedCancellationException(h5);
+            rs[5].assertNotInvoked();
+        }
+
+        checkCancelled(f);
+        checkCompletedNormally(g, v1);
+        checkCompletedWithWrappedCancellationException(h0);
+        checkCompletedWithWrappedCancellationException(h1);
+        checkCompletedWithWrappedCancellationException(h2);
+        checkCompletedWithWrappedCancellationException(h3);
+        for (int i = 0; i < 4; i++) rs[i].assertNotInvoked();
+    }}
+
+    public void testApplyToEither_sourceCancelled2() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+        for (boolean fFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final IncFunction[] rs = new IncFunction[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m);
+
+        final CompletableFuture<Integer> h0 = m.applyToEither(f, g, rs[0]);
+        final CompletableFuture<Integer> h1 = m.applyToEither(g, f, rs[1]);
+        assertTrue(fFirst ? f.complete(v1) : g.cancel(mayInterruptIfRunning));
+        assertTrue(!fFirst ? f.complete(v1) : g.cancel(mayInterruptIfRunning));
+        final CompletableFuture<Integer> h2 = m.applyToEither(f, g, rs[2]);
+        final CompletableFuture<Integer> h3 = m.applyToEither(g, f, rs[3]);
+
+        // unspecified behavior - both source completions available
+        try {
+            assertEquals(inc(v1), h0.join());
+            rs[0].assertValue(inc(v1));
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedCancellationException(h0);
+            rs[0].assertNotInvoked();
+        }
+        try {
+            assertEquals(inc(v1), h1.join());
+            rs[1].assertValue(inc(v1));
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedCancellationException(h1);
+            rs[1].assertNotInvoked();
+        }
+        try {
+            assertEquals(inc(v1), h2.join());
+            rs[2].assertValue(inc(v1));
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedCancellationException(h2);
+            rs[2].assertNotInvoked();
+        }
+        try {
+            assertEquals(inc(v1), h3.join());
+            rs[3].assertValue(inc(v1));
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedCancellationException(h3);
+            rs[3].assertNotInvoked();
+        }
+
+        checkCompletedNormally(f, v1);
+        checkCancelled(g);
+    }}
+
+    /**
+     * applyToEither result completes exceptionally if action does
+     */
+    public void testApplyToEither_actionFailed() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+        for (Integer v2 : new Integer[] { 2, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final FailingFunction[] rs = new FailingFunction[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new FailingFunction(m);
+
+        final CompletableFuture<Integer> h0 = m.applyToEither(f, g, rs[0]);
+        final CompletableFuture<Integer> h1 = m.applyToEither(g, f, rs[1]);
+        f.complete(v1);
+        final CompletableFuture<Integer> h2 = m.applyToEither(f, g, rs[2]);
+        final CompletableFuture<Integer> h3 = m.applyToEither(g, f, rs[3]);
+        checkCompletedWithWrappedCFException(h0);
+        checkCompletedWithWrappedCFException(h1);
+        checkCompletedWithWrappedCFException(h2);
+        checkCompletedWithWrappedCFException(h3);
+        for (int i = 0; i < 4; i++) rs[i].assertValue(v1);
+
+        g.complete(v2);
+
+        // unspecified behavior - both source completions available
+        final CompletableFuture<Integer> h4 = m.applyToEither(f, g, rs[4]);
+        final CompletableFuture<Integer> h5 = m.applyToEither(g, f, rs[5]);
+
+        checkCompletedWithWrappedCFException(h4);
+        assertTrue(Objects.equals(v1, rs[4].value) ||
+                   Objects.equals(v2, rs[4].value));
+        checkCompletedWithWrappedCFException(h5);
+        assertTrue(Objects.equals(v1, rs[5].value) ||
+                   Objects.equals(v2, rs[5].value));
+
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v2);
+    }}
+
+    /**
+     * acceptEither result completes normally after normal completion
+     * of either source
+     */
+    public void testAcceptEither_normalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+        for (Integer v2 : new Integer[] { 2, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final NoopConsumer[] rs = new NoopConsumer[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m);
+
+        final CompletableFuture<Void> h0 = m.acceptEither(f, g, rs[0]);
+        final CompletableFuture<Void> h1 = m.acceptEither(g, f, rs[1]);
+        checkIncomplete(h0);
+        checkIncomplete(h1);
+        rs[0].assertNotInvoked();
+        rs[1].assertNotInvoked();
+        f.complete(v1);
+        checkCompletedNormally(h0, null);
+        checkCompletedNormally(h1, null);
+        rs[0].assertValue(v1);
+        rs[1].assertValue(v1);
+        final CompletableFuture<Void> h2 = m.acceptEither(f, g, rs[2]);
+        final CompletableFuture<Void> h3 = m.acceptEither(g, f, rs[3]);
+        checkCompletedNormally(h2, null);
+        checkCompletedNormally(h3, null);
+        rs[2].assertValue(v1);
+        rs[3].assertValue(v1);
+        g.complete(v2);
+
+        // unspecified behavior - both source completions available
+        final CompletableFuture<Void> h4 = m.acceptEither(f, g, rs[4]);
+        final CompletableFuture<Void> h5 = m.acceptEither(g, f, rs[5]);
+        checkCompletedNormally(h4, null);
+        checkCompletedNormally(h5, null);
+        assertTrue(Objects.equals(v1, rs[4].value) ||
+                   Objects.equals(v2, rs[4].value));
+        assertTrue(Objects.equals(v1, rs[5].value) ||
+                   Objects.equals(v2, rs[5].value));
+
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v2);
+        checkCompletedNormally(h0, null);
+        checkCompletedNormally(h1, null);
+        checkCompletedNormally(h2, null);
+        checkCompletedNormally(h3, null);
+        for (int i = 0; i < 4; i++) rs[i].assertValue(v1);
+    }}
+
+    /**
+     * acceptEither result completes exceptionally after exceptional
+     * completion of either source
+     */
+    public void testAcceptEither_exceptionalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final CFException ex = new CFException();
+        final NoopConsumer[] rs = new NoopConsumer[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m);
+
+        final CompletableFuture<Void> h0 = m.acceptEither(f, g, rs[0]);
+        final CompletableFuture<Void> h1 = m.acceptEither(g, f, rs[1]);
+        checkIncomplete(h0);
+        checkIncomplete(h1);
+        rs[0].assertNotInvoked();
+        rs[1].assertNotInvoked();
+        f.completeExceptionally(ex);
+        checkCompletedWithWrappedException(h0, ex);
+        checkCompletedWithWrappedException(h1, ex);
+        final CompletableFuture<Void> h2 = m.acceptEither(f, g, rs[2]);
+        final CompletableFuture<Void> h3 = m.acceptEither(g, f, rs[3]);
+        checkCompletedWithWrappedException(h2, ex);
+        checkCompletedWithWrappedException(h3, ex);
+
+        g.complete(v1);
+
+        // unspecified behavior - both source completions available
+        final CompletableFuture<Void> h4 = m.acceptEither(f, g, rs[4]);
+        final CompletableFuture<Void> h5 = m.acceptEither(g, f, rs[5]);
+        try {
+            assertNull(h4.join());
+            rs[4].assertValue(v1);
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h4, ex);
+            rs[4].assertNotInvoked();
+        }
+        try {
+            assertNull(h5.join());
+            rs[5].assertValue(v1);
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h5, ex);
+            rs[5].assertNotInvoked();
+        }
+
+        checkCompletedExceptionally(f, ex);
+        checkCompletedNormally(g, v1);
+        checkCompletedWithWrappedException(h0, ex);
+        checkCompletedWithWrappedException(h1, ex);
+        checkCompletedWithWrappedException(h2, ex);
+        checkCompletedWithWrappedException(h3, ex);
+        checkCompletedWithWrappedException(h4, ex);
+        for (int i = 0; i < 4; i++) rs[i].assertNotInvoked();
+    }}
+
+    public void testAcceptEither_exceptionalCompletion2() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean fFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final CFException ex = new CFException();
+        final NoopConsumer[] rs = new NoopConsumer[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m);
+
+        final CompletableFuture<Void> h0 = m.acceptEither(f, g, rs[0]);
+        final CompletableFuture<Void> h1 = m.acceptEither(g, f, rs[1]);
+        assertTrue(fFirst ? f.complete(v1) : g.completeExceptionally(ex));
+        assertTrue(!fFirst ? f.complete(v1) : g.completeExceptionally(ex));
+        final CompletableFuture<Void> h2 = m.acceptEither(f, g, rs[2]);
+        final CompletableFuture<Void> h3 = m.acceptEither(g, f, rs[3]);
+
+        // unspecified behavior - both source completions available
+        try {
+            assertEquals(null, h0.join());
+            rs[0].assertValue(v1);
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h0, ex);
+            rs[0].assertNotInvoked();
+        }
+        try {
+            assertEquals(null, h1.join());
+            rs[1].assertValue(v1);
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h1, ex);
+            rs[1].assertNotInvoked();
+        }
+        try {
+            assertEquals(null, h2.join());
+            rs[2].assertValue(v1);
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h2, ex);
+            rs[2].assertNotInvoked();
+        }
+        try {
+            assertEquals(null, h3.join());
+            rs[3].assertValue(v1);
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h3, ex);
+            rs[3].assertNotInvoked();
+        }
+
+        checkCompletedNormally(f, v1);
+        checkCompletedExceptionally(g, ex);
+    }}
+
+    /**
+     * acceptEither result completes exceptionally if either source cancelled
+     */
+    public void testAcceptEither_sourceCancelled() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final NoopConsumer[] rs = new NoopConsumer[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m);
+
+        final CompletableFuture<Void> h0 = m.acceptEither(f, g, rs[0]);
+        final CompletableFuture<Void> h1 = m.acceptEither(g, f, rs[1]);
+        checkIncomplete(h0);
+        checkIncomplete(h1);
+        rs[0].assertNotInvoked();
+        rs[1].assertNotInvoked();
+        f.cancel(mayInterruptIfRunning);
+        checkCompletedWithWrappedCancellationException(h0);
+        checkCompletedWithWrappedCancellationException(h1);
+        final CompletableFuture<Void> h2 = m.acceptEither(f, g, rs[2]);
+        final CompletableFuture<Void> h3 = m.acceptEither(g, f, rs[3]);
+        checkCompletedWithWrappedCancellationException(h2);
+        checkCompletedWithWrappedCancellationException(h3);
+
+        g.complete(v1);
+
+        // unspecified behavior - both source completions available
+        final CompletableFuture<Void> h4 = m.acceptEither(f, g, rs[4]);
+        final CompletableFuture<Void> h5 = m.acceptEither(g, f, rs[5]);
+        try {
+            assertNull(h4.join());
+            rs[4].assertValue(v1);
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedCancellationException(h4);
+            rs[4].assertNotInvoked();
+        }
+        try {
+            assertNull(h5.join());
+            rs[5].assertValue(v1);
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedCancellationException(h5);
+            rs[5].assertNotInvoked();
+        }
+
+        checkCancelled(f);
+        checkCompletedNormally(g, v1);
+        checkCompletedWithWrappedCancellationException(h0);
+        checkCompletedWithWrappedCancellationException(h1);
+        checkCompletedWithWrappedCancellationException(h2);
+        checkCompletedWithWrappedCancellationException(h3);
+        for (int i = 0; i < 4; i++) rs[i].assertNotInvoked();
+    }}
+
+    /**
+     * acceptEither result completes exceptionally if action does
+     */
+    public void testAcceptEither_actionFailed() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+        for (Integer v2 : new Integer[] { 2, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final FailingConsumer[] rs = new FailingConsumer[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new FailingConsumer(m);
+
+        final CompletableFuture<Void> h0 = m.acceptEither(f, g, rs[0]);
+        final CompletableFuture<Void> h1 = m.acceptEither(g, f, rs[1]);
+        f.complete(v1);
+        final CompletableFuture<Void> h2 = m.acceptEither(f, g, rs[2]);
+        final CompletableFuture<Void> h3 = m.acceptEither(g, f, rs[3]);
+        checkCompletedWithWrappedCFException(h0);
+        checkCompletedWithWrappedCFException(h1);
+        checkCompletedWithWrappedCFException(h2);
+        checkCompletedWithWrappedCFException(h3);
+        for (int i = 0; i < 4; i++) rs[i].assertValue(v1);
+
+        g.complete(v2);
+
+        // unspecified behavior - both source completions available
+        final CompletableFuture<Void> h4 = m.acceptEither(f, g, rs[4]);
+        final CompletableFuture<Void> h5 = m.acceptEither(g, f, rs[5]);
+
+        checkCompletedWithWrappedCFException(h4);
+        assertTrue(Objects.equals(v1, rs[4].value) ||
+                   Objects.equals(v2, rs[4].value));
+        checkCompletedWithWrappedCFException(h5);
+        assertTrue(Objects.equals(v1, rs[5].value) ||
+                   Objects.equals(v2, rs[5].value));
+
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v2);
+    }}
+
+    /**
+     * runAfterEither result completes normally after normal completion
+     * of either source
+     */
+    public void testRunAfterEither_normalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+        for (Integer v2 : new Integer[] { 2, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final Noop[] rs = new Noop[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m);
+
+        final CompletableFuture<Void> h0 = m.runAfterEither(f, g, rs[0]);
+        final CompletableFuture<Void> h1 = m.runAfterEither(g, f, rs[1]);
+        checkIncomplete(h0);
+        checkIncomplete(h1);
+        rs[0].assertNotInvoked();
+        rs[1].assertNotInvoked();
+        f.complete(v1);
+        checkCompletedNormally(h0, null);
+        checkCompletedNormally(h1, null);
+        rs[0].assertInvoked();
+        rs[1].assertInvoked();
+        final CompletableFuture<Void> h2 = m.runAfterEither(f, g, rs[2]);
+        final CompletableFuture<Void> h3 = m.runAfterEither(g, f, rs[3]);
+        checkCompletedNormally(h2, null);
+        checkCompletedNormally(h3, null);
+        rs[2].assertInvoked();
+        rs[3].assertInvoked();
+
+        g.complete(v2);
+
+        final CompletableFuture<Void> h4 = m.runAfterEither(f, g, rs[4]);
+        final CompletableFuture<Void> h5 = m.runAfterEither(g, f, rs[5]);
+
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v2);
+        checkCompletedNormally(h0, null);
+        checkCompletedNormally(h1, null);
+        checkCompletedNormally(h2, null);
+        checkCompletedNormally(h3, null);
+        checkCompletedNormally(h4, null);
+        checkCompletedNormally(h5, null);
+        for (int i = 0; i < 6; i++) rs[i].assertInvoked();
+    }}
+
+    /**
+     * runAfterEither result completes exceptionally after exceptional
+     * completion of either source
+     */
+    public void testRunAfterEither_exceptionalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final CFException ex = new CFException();
+        final Noop[] rs = new Noop[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m);
+
+        final CompletableFuture<Void> h0 = m.runAfterEither(f, g, rs[0]);
+        final CompletableFuture<Void> h1 = m.runAfterEither(g, f, rs[1]);
+        checkIncomplete(h0);
+        checkIncomplete(h1);
+        rs[0].assertNotInvoked();
+        rs[1].assertNotInvoked();
+        assertTrue(f.completeExceptionally(ex));
+        checkCompletedWithWrappedException(h0, ex);
+        checkCompletedWithWrappedException(h1, ex);
+        final CompletableFuture<Void> h2 = m.runAfterEither(f, g, rs[2]);
+        final CompletableFuture<Void> h3 = m.runAfterEither(g, f, rs[3]);
+        checkCompletedWithWrappedException(h2, ex);
+        checkCompletedWithWrappedException(h3, ex);
+
+        assertTrue(g.complete(v1));
+
+        // unspecified behavior - both source completions available
+        final CompletableFuture<Void> h4 = m.runAfterEither(f, g, rs[4]);
+        final CompletableFuture<Void> h5 = m.runAfterEither(g, f, rs[5]);
+        try {
+            assertNull(h4.join());
+            rs[4].assertInvoked();
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h4, ex);
+            rs[4].assertNotInvoked();
+        }
+        try {
+            assertNull(h5.join());
+            rs[5].assertInvoked();
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h5, ex);
+            rs[5].assertNotInvoked();
+        }
+
+        checkCompletedExceptionally(f, ex);
+        checkCompletedNormally(g, v1);
+        checkCompletedWithWrappedException(h0, ex);
+        checkCompletedWithWrappedException(h1, ex);
+        checkCompletedWithWrappedException(h2, ex);
+        checkCompletedWithWrappedException(h3, ex);
+        checkCompletedWithWrappedException(h4, ex);
+        for (int i = 0; i < 4; i++) rs[i].assertNotInvoked();
+    }}
+
+    public void testRunAfterEither_exceptionalCompletion2() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean fFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final CFException ex = new CFException();
+        final Noop[] rs = new Noop[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m);
+
+        final CompletableFuture<Void> h0 = m.runAfterEither(f, g, rs[0]);
+        final CompletableFuture<Void> h1 = m.runAfterEither(g, f, rs[1]);
+        assertTrue( fFirst ? f.complete(v1) : g.completeExceptionally(ex));
+        assertTrue(!fFirst ? f.complete(v1) : g.completeExceptionally(ex));
+        final CompletableFuture<Void> h2 = m.runAfterEither(f, g, rs[2]);
+        final CompletableFuture<Void> h3 = m.runAfterEither(g, f, rs[3]);
+
+        // unspecified behavior - both source completions available
+        try {
+            assertEquals(null, h0.join());
+            rs[0].assertInvoked();
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h0, ex);
+            rs[0].assertNotInvoked();
+        }
+        try {
+            assertEquals(null, h1.join());
+            rs[1].assertInvoked();
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h1, ex);
+            rs[1].assertNotInvoked();
+        }
+        try {
+            assertEquals(null, h2.join());
+            rs[2].assertInvoked();
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h2, ex);
+            rs[2].assertNotInvoked();
+        }
+        try {
+            assertEquals(null, h3.join());
+            rs[3].assertInvoked();
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h3, ex);
+            rs[3].assertNotInvoked();
+        }
+
+        checkCompletedNormally(f, v1);
+        checkCompletedExceptionally(g, ex);
+    }}
+
+    /**
+     * runAfterEither result completes exceptionally if either source cancelled
+     */
+    public void testRunAfterEither_sourceCancelled() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final Noop[] rs = new Noop[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m);
+
+        final CompletableFuture<Void> h0 = m.runAfterEither(f, g, rs[0]);
+        final CompletableFuture<Void> h1 = m.runAfterEither(g, f, rs[1]);
+        checkIncomplete(h0);
+        checkIncomplete(h1);
+        rs[0].assertNotInvoked();
+        rs[1].assertNotInvoked();
+        f.cancel(mayInterruptIfRunning);
+        checkCompletedWithWrappedCancellationException(h0);
+        checkCompletedWithWrappedCancellationException(h1);
+        final CompletableFuture<Void> h2 = m.runAfterEither(f, g, rs[2]);
+        final CompletableFuture<Void> h3 = m.runAfterEither(g, f, rs[3]);
+        checkCompletedWithWrappedCancellationException(h2);
+        checkCompletedWithWrappedCancellationException(h3);
+
+        assertTrue(g.complete(v1));
+
+        // unspecified behavior - both source completions available
+        final CompletableFuture<Void> h4 = m.runAfterEither(f, g, rs[4]);
+        final CompletableFuture<Void> h5 = m.runAfterEither(g, f, rs[5]);
+        try {
+            assertNull(h4.join());
+            rs[4].assertInvoked();
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedCancellationException(h4);
+            rs[4].assertNotInvoked();
+        }
+        try {
+            assertNull(h5.join());
+            rs[5].assertInvoked();
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedCancellationException(h5);
+            rs[5].assertNotInvoked();
+        }
+
+        checkCancelled(f);
+        checkCompletedNormally(g, v1);
+        checkCompletedWithWrappedCancellationException(h0);
+        checkCompletedWithWrappedCancellationException(h1);
+        checkCompletedWithWrappedCancellationException(h2);
+        checkCompletedWithWrappedCancellationException(h3);
+        for (int i = 0; i < 4; i++) rs[i].assertNotInvoked();
+    }}
+
+    /**
+     * runAfterEither result completes exceptionally if action does
+     */
+    public void testRunAfterEither_actionFailed() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+        for (Integer v2 : new Integer[] { 2, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final FailingRunnable[] rs = new FailingRunnable[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new FailingRunnable(m);
+
+        final CompletableFuture<Void> h0 = m.runAfterEither(f, g, rs[0]);
+        final CompletableFuture<Void> h1 = m.runAfterEither(g, f, rs[1]);
+        assertTrue(f.complete(v1));
+        final CompletableFuture<Void> h2 = m.runAfterEither(f, g, rs[2]);
+        final CompletableFuture<Void> h3 = m.runAfterEither(g, f, rs[3]);
+        checkCompletedWithWrappedCFException(h0);
+        checkCompletedWithWrappedCFException(h1);
+        checkCompletedWithWrappedCFException(h2);
+        checkCompletedWithWrappedCFException(h3);
+        for (int i = 0; i < 4; i++) rs[i].assertInvoked();
+        assertTrue(g.complete(v2));
+        final CompletableFuture<Void> h4 = m.runAfterEither(f, g, rs[4]);
+        final CompletableFuture<Void> h5 = m.runAfterEither(g, f, rs[5]);
+        checkCompletedWithWrappedCFException(h4);
+        checkCompletedWithWrappedCFException(h5);
+
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v2);
+        for (int i = 0; i < 6; i++) rs[i].assertInvoked();
+    }}
+
+    /**
+     * thenCompose result completes normally after normal completion of source
+     */
+    public void testThenCompose_normalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFutureInc r = new CompletableFutureInc(m);
+        if (!createIncomplete) assertTrue(f.complete(v1));
+        final CompletableFuture<Integer> g = m.thenCompose(f, r);
+        if (createIncomplete) assertTrue(f.complete(v1));
+
+        checkCompletedNormally(g, inc(v1));
+        checkCompletedNormally(f, v1);
+        r.assertValue(v1);
+    }}
+
+    /**
+     * thenCompose result completes exceptionally after exceptional
+     * completion of source
+     */
+    public void testThenCompose_exceptionalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean createIncomplete : new boolean[] { true, false })
+    {
+        final CFException ex = new CFException();
+        final CompletableFutureInc r = new CompletableFutureInc(m);
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        if (!createIncomplete) f.completeExceptionally(ex);
+        final CompletableFuture<Integer> g = m.thenCompose(f, r);
+        if (createIncomplete) f.completeExceptionally(ex);
+
+        checkCompletedWithWrappedException(g, ex);
+        checkCompletedExceptionally(f, ex);
+        r.assertNotInvoked();
+    }}
+
+    /**
+     * thenCompose result completes exceptionally if action does
+     */
+    public void testThenCompose_actionFailed() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final FailingCompletableFutureFunction r
+            = new FailingCompletableFutureFunction(m);
+        if (!createIncomplete) assertTrue(f.complete(v1));
+        final CompletableFuture<Integer> g = m.thenCompose(f, r);
+        if (createIncomplete) assertTrue(f.complete(v1));
+
+        checkCompletedWithWrappedCFException(g);
+        checkCompletedNormally(f, v1);
+    }}
+
+    /**
+     * thenCompose result completes exceptionally if source cancelled
+     */
+    public void testThenCompose_sourceCancelled() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFutureInc r = new CompletableFutureInc(m);
+        if (!createIncomplete) assertTrue(f.cancel(mayInterruptIfRunning));
+        final CompletableFuture<Integer> g = m.thenCompose(f, r);
+        if (createIncomplete) {
+            checkIncomplete(g);
+            assertTrue(f.cancel(mayInterruptIfRunning));
+        }
+
+        checkCompletedWithWrappedCancellationException(g);
+        checkCancelled(f);
+    }}
+
+    /**
+     * thenCompose result completes exceptionally if the result of the action does
+     */
+    public void testThenCompose_actionReturnsFailingFuture() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (int order = 0; order < 6; order++)
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CFException ex = new CFException();
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final CompletableFuture<Integer> h;
+        // Test all permutations of orders
+        switch (order) {
+        case 0:
+            assertTrue(f.complete(v1));
+            assertTrue(g.completeExceptionally(ex));
+            h = m.thenCompose(f, (x -> g));
+            break;
+        case 1:
+            assertTrue(f.complete(v1));
+            h = m.thenCompose(f, (x -> g));
+            assertTrue(g.completeExceptionally(ex));
+            break;
+        case 2:
+            assertTrue(g.completeExceptionally(ex));
+            assertTrue(f.complete(v1));
+            h = m.thenCompose(f, (x -> g));
+            break;
+        case 3:
+            assertTrue(g.completeExceptionally(ex));
+            h = m.thenCompose(f, (x -> g));
+            assertTrue(f.complete(v1));
+            break;
+        case 4:
+            h = m.thenCompose(f, (x -> g));
+            assertTrue(f.complete(v1));
+            assertTrue(g.completeExceptionally(ex));
+            break;
+        case 5:
+            h = m.thenCompose(f, (x -> g));
+            assertTrue(f.complete(v1));
+            assertTrue(g.completeExceptionally(ex));
+            break;
+        default: throw new AssertionError();
+        }
+
+        checkCompletedExceptionally(g, ex);
+        checkCompletedWithWrappedException(h, ex);
+        checkCompletedNormally(f, v1);
+    }}
+
+    // other static methods
+
+    /**
+     * allOf(no component futures) returns a future completed normally
+     * with the value null
+     */
+    public void testAllOf_empty() throws Exception {
+        CompletableFuture<Void> f = CompletableFuture.allOf();
+        checkCompletedNormally(f, null);
+    }
+
+    /**
+     * allOf returns a future completed normally with the value null
+     * when all components complete normally
+     */
+    public void testAllOf_normal() throws Exception {
+        for (int k = 1; k < 10; k++) {
+            CompletableFuture<Integer>[] fs
+                = (CompletableFuture<Integer>[]) new CompletableFuture[k];
+            for (int i = 0; i < k; i++)
+                fs[i] = new CompletableFuture<>();
+            CompletableFuture<Void> f = CompletableFuture.allOf(fs);
+            for (int i = 0; i < k; i++) {
+                checkIncomplete(f);
+                checkIncomplete(CompletableFuture.allOf(fs));
+                fs[i].complete(one);
+            }
+            checkCompletedNormally(f, null);
+            checkCompletedNormally(CompletableFuture.allOf(fs), null);
+        }
+    }
+
+    public void testAllOf_backwards() throws Exception {
+        for (int k = 1; k < 10; k++) {
+            CompletableFuture<Integer>[] fs
+                = (CompletableFuture<Integer>[]) new CompletableFuture[k];
+            for (int i = 0; i < k; i++)
+                fs[i] = new CompletableFuture<>();
+            CompletableFuture<Void> f = CompletableFuture.allOf(fs);
+            for (int i = k - 1; i >= 0; i--) {
+                checkIncomplete(f);
+                checkIncomplete(CompletableFuture.allOf(fs));
+                fs[i].complete(one);
+            }
+            checkCompletedNormally(f, null);
+            checkCompletedNormally(CompletableFuture.allOf(fs), null);
+        }
+    }
+
+    public void testAllOf_exceptional() throws Exception {
+        for (int k = 1; k < 10; k++) {
+            CompletableFuture<Integer>[] fs
+                = (CompletableFuture<Integer>[]) new CompletableFuture[k];
+            CFException ex = new CFException();
+            for (int i = 0; i < k; i++)
+                fs[i] = new CompletableFuture<>();
+            CompletableFuture<Void> f = CompletableFuture.allOf(fs);
+            for (int i = 0; i < k; i++) {
+                checkIncomplete(f);
+                checkIncomplete(CompletableFuture.allOf(fs));
+                if (i != k / 2) {
+                    fs[i].complete(i);
+                    checkCompletedNormally(fs[i], i);
+                } else {
+                    fs[i].completeExceptionally(ex);
+                    checkCompletedExceptionally(fs[i], ex);
+                }
+            }
+            checkCompletedWithWrappedException(f, ex);
+            checkCompletedWithWrappedException(CompletableFuture.allOf(fs), ex);
+        }
+    }
+
+    /**
+     * anyOf(no component futures) returns an incomplete future
+     */
+    public void testAnyOf_empty() throws Exception {
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        CompletableFuture<Object> f = CompletableFuture.anyOf();
+        checkIncomplete(f);
+
+        f.complete(v1);
+        checkCompletedNormally(f, v1);
+    }}
+
+    /**
+     * anyOf returns a future completed normally with a value when
+     * a component future does
+     */
+    public void testAnyOf_normal() throws Exception {
+        for (int k = 0; k < 10; k++) {
+            CompletableFuture[] fs = new CompletableFuture[k];
+            for (int i = 0; i < k; i++)
+                fs[i] = new CompletableFuture<>();
+            CompletableFuture<Object> f = CompletableFuture.anyOf(fs);
+            checkIncomplete(f);
+            for (int i = 0; i < k; i++) {
+                fs[i].complete(i);
+                checkCompletedNormally(f, 0);
+                int x = (int) CompletableFuture.anyOf(fs).join();
+                assertTrue(0 <= x && x <= i);
+            }
+        }
+    }
+    public void testAnyOf_normal_backwards() throws Exception {
+        for (int k = 0; k < 10; k++) {
+            CompletableFuture[] fs = new CompletableFuture[k];
+            for (int i = 0; i < k; i++)
+                fs[i] = new CompletableFuture<>();
+            CompletableFuture<Object> f = CompletableFuture.anyOf(fs);
+            checkIncomplete(f);
+            for (int i = k - 1; i >= 0; i--) {
+                fs[i].complete(i);
+                checkCompletedNormally(f, k - 1);
+                int x = (int) CompletableFuture.anyOf(fs).join();
+                assertTrue(i <= x && x <= k - 1);
+            }
+        }
+    }
+
+    /**
+     * anyOf result completes exceptionally when any component does.
+     */
+    public void testAnyOf_exceptional() throws Exception {
+        for (int k = 0; k < 10; k++) {
+            CompletableFuture[] fs = new CompletableFuture[k];
+            CFException[] exs = new CFException[k];
+            for (int i = 0; i < k; i++) {
+                fs[i] = new CompletableFuture<>();
+                exs[i] = new CFException();
+            }
+            CompletableFuture<Object> f = CompletableFuture.anyOf(fs);
+            checkIncomplete(f);
+            for (int i = 0; i < k; i++) {
+                fs[i].completeExceptionally(exs[i]);
+                checkCompletedWithWrappedException(f, exs[0]);
+                checkCompletedWithWrappedCFException(CompletableFuture.anyOf(fs));
+            }
+        }
+    }
+
+    public void testAnyOf_exceptional_backwards() throws Exception {
+        for (int k = 0; k < 10; k++) {
+            CompletableFuture[] fs = new CompletableFuture[k];
+            CFException[] exs = new CFException[k];
+            for (int i = 0; i < k; i++) {
+                fs[i] = new CompletableFuture<>();
+                exs[i] = new CFException();
+            }
+            CompletableFuture<Object> f = CompletableFuture.anyOf(fs);
+            checkIncomplete(f);
+            for (int i = k - 1; i >= 0; i--) {
+                fs[i].completeExceptionally(exs[i]);
+                checkCompletedWithWrappedException(f, exs[k - 1]);
+                checkCompletedWithWrappedCFException(CompletableFuture.anyOf(fs));
+            }
+        }
+    }
+
+    /**
+     * Completion methods throw NullPointerException with null arguments
+     */
+    public void testNPE() {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        CompletableFuture<Integer> g = new CompletableFuture<>();
+        CompletableFuture<Integer> nullFuture = (CompletableFuture<Integer>)null;
+        ThreadExecutor exec = new ThreadExecutor();
+
+        Runnable[] throwingActions = {
+            () -> CompletableFuture.supplyAsync(null),
+            () -> CompletableFuture.supplyAsync(null, exec),
+            () -> CompletableFuture.supplyAsync(new IntegerSupplier(ExecutionMode.SYNC, 42), null),
+
+            () -> CompletableFuture.runAsync(null),
+            () -> CompletableFuture.runAsync(null, exec),
+            () -> CompletableFuture.runAsync(() -> {}, null),
+
+            () -> f.completeExceptionally(null),
+
+            () -> f.thenApply(null),
+            () -> f.thenApplyAsync(null),
+            () -> f.thenApplyAsync((x) -> x, null),
+            () -> f.thenApplyAsync(null, exec),
+
+            () -> f.thenAccept(null),
+            () -> f.thenAcceptAsync(null),
+            () -> f.thenAcceptAsync((x) -> {} , null),
+            () -> f.thenAcceptAsync(null, exec),
+
+            () -> f.thenRun(null),
+            () -> f.thenRunAsync(null),
+            () -> f.thenRunAsync(() -> {} , null),
+            () -> f.thenRunAsync(null, exec),
+
+            () -> f.thenCombine(g, null),
+            () -> f.thenCombineAsync(g, null),
+            () -> f.thenCombineAsync(g, null, exec),
+            () -> f.thenCombine(nullFuture, (x, y) -> x),
+            () -> f.thenCombineAsync(nullFuture, (x, y) -> x),
+            () -> f.thenCombineAsync(nullFuture, (x, y) -> x, exec),
+            () -> f.thenCombineAsync(g, (x, y) -> x, null),
+
+            () -> f.thenAcceptBoth(g, null),
+            () -> f.thenAcceptBothAsync(g, null),
+            () -> f.thenAcceptBothAsync(g, null, exec),
+            () -> f.thenAcceptBoth(nullFuture, (x, y) -> {}),
+            () -> f.thenAcceptBothAsync(nullFuture, (x, y) -> {}),
+            () -> f.thenAcceptBothAsync(nullFuture, (x, y) -> {}, exec),
+            () -> f.thenAcceptBothAsync(g, (x, y) -> {}, null),
+
+            () -> f.runAfterBoth(g, null),
+            () -> f.runAfterBothAsync(g, null),
+            () -> f.runAfterBothAsync(g, null, exec),
+            () -> f.runAfterBoth(nullFuture, () -> {}),
+            () -> f.runAfterBothAsync(nullFuture, () -> {}),
+            () -> f.runAfterBothAsync(nullFuture, () -> {}, exec),
+            () -> f.runAfterBothAsync(g, () -> {}, null),
+
+            () -> f.applyToEither(g, null),
+            () -> f.applyToEitherAsync(g, null),
+            () -> f.applyToEitherAsync(g, null, exec),
+            () -> f.applyToEither(nullFuture, (x) -> x),
+            () -> f.applyToEitherAsync(nullFuture, (x) -> x),
+            () -> f.applyToEitherAsync(nullFuture, (x) -> x, exec),
+            () -> f.applyToEitherAsync(g, (x) -> x, null),
+
+            () -> f.acceptEither(g, null),
+            () -> f.acceptEitherAsync(g, null),
+            () -> f.acceptEitherAsync(g, null, exec),
+            () -> f.acceptEither(nullFuture, (x) -> {}),
+            () -> f.acceptEitherAsync(nullFuture, (x) -> {}),
+            () -> f.acceptEitherAsync(nullFuture, (x) -> {}, exec),
+            () -> f.acceptEitherAsync(g, (x) -> {}, null),
+
+            () -> f.runAfterEither(g, null),
+            () -> f.runAfterEitherAsync(g, null),
+            () -> f.runAfterEitherAsync(g, null, exec),
+            () -> f.runAfterEither(nullFuture, () -> {}),
+            () -> f.runAfterEitherAsync(nullFuture, () -> {}),
+            () -> f.runAfterEitherAsync(nullFuture, () -> {}, exec),
+            () -> f.runAfterEitherAsync(g, () -> {}, null),
+
+            () -> f.thenCompose(null),
+            () -> f.thenComposeAsync(null),
+            () -> f.thenComposeAsync(new CompletableFutureInc(ExecutionMode.EXECUTOR), null),
+            () -> f.thenComposeAsync(null, exec),
+
+            () -> f.exceptionally(null),
+
+            () -> f.handle(null),
+
+            () -> CompletableFuture.allOf((CompletableFuture<?>)null),
+            () -> CompletableFuture.allOf((CompletableFuture<?>[])null),
+            () -> CompletableFuture.allOf(f, null),
+            () -> CompletableFuture.allOf(null, f),
+
+            () -> CompletableFuture.anyOf((CompletableFuture<?>)null),
+            () -> CompletableFuture.anyOf((CompletableFuture<?>[])null),
+            () -> CompletableFuture.anyOf(f, null),
+            () -> CompletableFuture.anyOf(null, f),
+
+            () -> f.obtrudeException(null),
+
+            () -> CompletableFuture.delayedExecutor(1L, SECONDS, null),
+            () -> CompletableFuture.delayedExecutor(1L, null, new ThreadExecutor()),
+            () -> CompletableFuture.delayedExecutor(1L, null),
+
+            () -> f.orTimeout(1L, null),
+            () -> f.completeOnTimeout(42, 1L, null),
+
+            () -> CompletableFuture.failedFuture(null),
+            () -> CompletableFuture.failedStage(null),
+        };
+
+        assertThrows(NullPointerException.class, throwingActions);
+        assertEquals(0, exec.count.get());
+    }
+
+    /**
+     * toCompletableFuture returns this CompletableFuture.
+     */
+    public void testToCompletableFuture() {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        assertSame(f, f.toCompletableFuture());
+    }
+
+    // jdk9
+
+    /**
+     * newIncompleteFuture returns an incomplete CompletableFuture
+     */
+    public void testNewIncompleteFuture() {
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        CompletableFuture<Integer> g = f.newIncompleteFuture();
+        checkIncomplete(f);
+        checkIncomplete(g);
+        f.complete(v1);
+        checkCompletedNormally(f, v1);
+        checkIncomplete(g);
+        g.complete(v1);
+        checkCompletedNormally(g, v1);
+        assertSame(g.getClass(), CompletableFuture.class);
+    }}
+
+    /**
+     * completedStage returns a completed CompletionStage
+     */
+    public void testCompletedStage() {
+        AtomicInteger x = new AtomicInteger(0);
+        AtomicReference<Throwable> r = new AtomicReference<Throwable>();
+        CompletionStage<Integer> f = CompletableFuture.completedStage(1);
+        f.whenComplete((v, e) -> {if (e != null) r.set(e); else x.set(v);});
+        assertEquals(x.get(), 1);
+        assertNull(r.get());
+    }
+
+    /**
+     * defaultExecutor by default returns the commonPool if
+     * it supports more than one thread.
+     */
+    public void testDefaultExecutor() {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        Executor e = f.defaultExecutor();
+        Executor c = ForkJoinPool.commonPool();
+        if (ForkJoinPool.getCommonPoolParallelism() > 1)
+            assertSame(e, c);
+        else
+            assertNotSame(e, c);
+    }
+
+    /**
+     * failedFuture returns a CompletableFuture completed
+     * exceptionally with the given Exception
+     */
+    public void testFailedFuture() {
+        CFException ex = new CFException();
+        CompletableFuture<Integer> f = CompletableFuture.failedFuture(ex);
+        checkCompletedExceptionally(f, ex);
+    }
+
+    /**
+     * failedFuture(null) throws NPE
+     */
+    public void testFailedFuture_null() {
+        try {
+            CompletableFuture<Integer> f = CompletableFuture.failedFuture(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * copy returns a CompletableFuture that is completed normally,
+     * with the same value, when source is.
+     */
+    public void testCopy() {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        CompletableFuture<Integer> g = f.copy();
+        checkIncomplete(f);
+        checkIncomplete(g);
+        f.complete(1);
+        checkCompletedNormally(f, 1);
+        checkCompletedNormally(g, 1);
+    }
+
+    /**
+     * copy returns a CompletableFuture that is completed exceptionally
+     * when source is.
+     */
+    public void testCopy2() {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        CompletableFuture<Integer> g = f.copy();
+        checkIncomplete(f);
+        checkIncomplete(g);
+        CFException ex = new CFException();
+        f.completeExceptionally(ex);
+        checkCompletedExceptionally(f, ex);
+        checkCompletedWithWrappedException(g, ex);
+    }
+
+    /**
+     * minimalCompletionStage returns a CompletableFuture that is
+     * completed normally, with the same value, when source is.
+     */
+    public void testMinimalCompletionStage() {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        CompletionStage<Integer> g = f.minimalCompletionStage();
+        AtomicInteger x = new AtomicInteger(0);
+        AtomicReference<Throwable> r = new AtomicReference<Throwable>();
+        checkIncomplete(f);
+        g.whenComplete((v, e) -> {if (e != null) r.set(e); else x.set(v);});
+        f.complete(1);
+        checkCompletedNormally(f, 1);
+        assertEquals(x.get(), 1);
+        assertNull(r.get());
+    }
+
+    /**
+     * minimalCompletionStage returns a CompletableFuture that is
+     * completed exceptionally when source is.
+     */
+    public void testMinimalCompletionStage2() {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        CompletionStage<Integer> g = f.minimalCompletionStage();
+        AtomicInteger x = new AtomicInteger(0);
+        AtomicReference<Throwable> r = new AtomicReference<Throwable>();
+        g.whenComplete((v, e) -> {if (e != null) r.set(e); else x.set(v);});
+        checkIncomplete(f);
+        CFException ex = new CFException();
+        f.completeExceptionally(ex);
+        checkCompletedExceptionally(f, ex);
+        assertEquals(x.get(), 0);
+        assertEquals(r.get().getCause(), ex);
+    }
+
+    /**
+     * failedStage returns a CompletionStage completed
+     * exceptionally with the given Exception
+     */
+    public void testFailedStage() {
+        CFException ex = new CFException();
+        CompletionStage<Integer> f = CompletableFuture.failedStage(ex);
+        AtomicInteger x = new AtomicInteger(0);
+        AtomicReference<Throwable> r = new AtomicReference<Throwable>();
+        f.whenComplete((v, e) -> {if (e != null) r.set(e); else x.set(v);});
+        assertEquals(x.get(), 0);
+        assertEquals(r.get(), ex);
+    }
+
+    /**
+     * completeAsync completes with value of given supplier
+     */
+    public void testCompleteAsync() {
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        f.completeAsync(() -> v1);
+        f.join();
+        checkCompletedNormally(f, v1);
+    }}
+
+    /**
+     * completeAsync completes exceptionally if given supplier throws
+     */
+    public void testCompleteAsync2() {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        CFException ex = new CFException();
+        f.completeAsync(() -> {if (true) throw ex; return 1;});
+        try {
+            f.join();
+            shouldThrow();
+        } catch (CompletionException success) {}
+        checkCompletedWithWrappedException(f, ex);
+    }
+
+    /**
+     * completeAsync with given executor completes with value of given supplier
+     */
+    public void testCompleteAsync3() {
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        ThreadExecutor executor = new ThreadExecutor();
+        f.completeAsync(() -> v1, executor);
+        assertSame(v1, f.join());
+        checkCompletedNormally(f, v1);
+        assertEquals(1, executor.count.get());
+    }}
+
+    /**
+     * completeAsync with given executor completes exceptionally if
+     * given supplier throws
+     */
+    public void testCompleteAsync4() {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        CFException ex = new CFException();
+        ThreadExecutor executor = new ThreadExecutor();
+        f.completeAsync(() -> {if (true) throw ex; return 1;}, executor);
+        try {
+            f.join();
+            shouldThrow();
+        } catch (CompletionException success) {}
+        checkCompletedWithWrappedException(f, ex);
+        assertEquals(1, executor.count.get());
+    }
+
+    /**
+     * orTimeout completes with TimeoutException if not complete
+     */
+    public void testOrTimeout_timesOut() {
+        long timeoutMillis = timeoutMillis();
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        long startTime = System.nanoTime();
+        f.orTimeout(timeoutMillis, MILLISECONDS);
+        checkCompletedWithTimeoutException(f);
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+    }
+
+    /**
+     * orTimeout completes normally if completed before timeout
+     */
+    public void testOrTimeout_completed() {
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        CompletableFuture<Integer> g = new CompletableFuture<>();
+        long startTime = System.nanoTime();
+        f.complete(v1);
+        f.orTimeout(LONG_DELAY_MS, MILLISECONDS);
+        g.orTimeout(LONG_DELAY_MS, MILLISECONDS);
+        g.complete(v1);
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v1);
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS / 2);
+    }}
+
+    /**
+     * completeOnTimeout completes with given value if not complete
+     */
+    public void testCompleteOnTimeout_timesOut() {
+        testInParallel(() -> testCompleteOnTimeout_timesOut(42),
+                       () -> testCompleteOnTimeout_timesOut(null));
+    }
+
+    public void testCompleteOnTimeout_timesOut(Integer v) {
+        long timeoutMillis = timeoutMillis();
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        long startTime = System.nanoTime();
+        f.completeOnTimeout(v, timeoutMillis, MILLISECONDS);
+        assertSame(v, f.join());
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+        f.complete(99);         // should have no effect
+        checkCompletedNormally(f, v);
+    }
+
+    /**
+     * completeOnTimeout has no effect if completed within timeout
+     */
+    public void testCompleteOnTimeout_completed() {
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        CompletableFuture<Integer> g = new CompletableFuture<>();
+        long startTime = System.nanoTime();
+        f.complete(v1);
+        f.completeOnTimeout(-1, LONG_DELAY_MS, MILLISECONDS);
+        g.completeOnTimeout(-1, LONG_DELAY_MS, MILLISECONDS);
+        g.complete(v1);
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v1);
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS / 2);
+    }}
+
+    /**
+     * delayedExecutor returns an executor that delays submission
+     */
+    public void testDelayedExecutor() {
+        testInParallel(() -> testDelayedExecutor(null, null),
+                       () -> testDelayedExecutor(null, 1),
+                       () -> testDelayedExecutor(new ThreadExecutor(), 1),
+                       () -> testDelayedExecutor(new ThreadExecutor(), 1));
+    }
+
+    public void testDelayedExecutor(Executor executor, Integer v) throws Exception {
+        long timeoutMillis = timeoutMillis();
+        // Use an "unreasonably long" long timeout to catch lingering threads
+        long longTimeoutMillis = 1000 * 60 * 60 * 24;
+        final Executor delayer, longDelayer;
+        if (executor == null) {
+            delayer = CompletableFuture.delayedExecutor(timeoutMillis, MILLISECONDS);
+            longDelayer = CompletableFuture.delayedExecutor(longTimeoutMillis, MILLISECONDS);
+        } else {
+            delayer = CompletableFuture.delayedExecutor(timeoutMillis, MILLISECONDS, executor);
+            longDelayer = CompletableFuture.delayedExecutor(longTimeoutMillis, MILLISECONDS, executor);
+        }
+        long startTime = System.nanoTime();
+        CompletableFuture<Integer> f =
+            CompletableFuture.supplyAsync(() -> v, delayer);
+        CompletableFuture<Integer> g =
+            CompletableFuture.supplyAsync(() -> v, longDelayer);
+
+        assertNull(g.getNow(null));
+
+        assertSame(v, f.get(LONG_DELAY_MS, MILLISECONDS));
+        long millisElapsed = millisElapsedSince(startTime);
+        assertTrue(millisElapsed >= timeoutMillis);
+        assertTrue(millisElapsed < LONG_DELAY_MS / 2);
+
+        checkCompletedNormally(f, v);
+
+        checkIncomplete(g);
+        assertTrue(g.cancel(true));
+    }
+
+    //--- tests of implementation details; not part of official tck ---
+
+    Object resultOf(CompletableFuture<?> f) {
+        try {
+            java.lang.reflect.Field resultField
+                = CompletableFuture.class.getDeclaredField("result");
+            resultField.setAccessible(true);
+            return resultField.get(f);
+        } catch (Throwable t) { throw new AssertionError(t); }
+    }
+
+    public void testExceptionPropagationReusesResultObject() {
+        if (!testImplementationDetails) return;
+        for (ExecutionMode m : ExecutionMode.values())
+    {
+        final CFException ex = new CFException();
+        final CompletableFuture<Integer> v42 = CompletableFuture.completedFuture(42);
+        final CompletableFuture<Integer> incomplete = new CompletableFuture<>();
+
+        List<Function<CompletableFuture<Integer>, CompletableFuture<?>>> funs
+            = new ArrayList<>();
+
+        funs.add((y) -> m.thenRun(y, new Noop(m)));
+        funs.add((y) -> m.thenAccept(y, new NoopConsumer(m)));
+        funs.add((y) -> m.thenApply(y, new IncFunction(m)));
+
+        funs.add((y) -> m.runAfterEither(y, incomplete, new Noop(m)));
+        funs.add((y) -> m.acceptEither(y, incomplete, new NoopConsumer(m)));
+        funs.add((y) -> m.applyToEither(y, incomplete, new IncFunction(m)));
+
+        funs.add((y) -> m.runAfterBoth(y, v42, new Noop(m)));
+        funs.add((y) -> m.thenAcceptBoth(y, v42, new SubtractAction(m)));
+        funs.add((y) -> m.thenCombine(y, v42, new SubtractFunction(m)));
+
+        funs.add((y) -> m.whenComplete(y, (Integer r, Throwable t) -> {}));
+
+        funs.add((y) -> m.thenCompose(y, new CompletableFutureInc(m)));
+
+        funs.add((y) -> CompletableFuture.allOf(new CompletableFuture<?>[] {y, v42}));
+        funs.add((y) -> CompletableFuture.anyOf(new CompletableFuture<?>[] {y, incomplete}));
+
+        for (Function<CompletableFuture<Integer>, CompletableFuture<?>>
+                 fun : funs) {
+            CompletableFuture<Integer> f = new CompletableFuture<>();
+            f.completeExceptionally(ex);
+            CompletableFuture<Integer> src = m.thenApply(f, new IncFunction(m));
+            checkCompletedWithWrappedException(src, ex);
+            CompletableFuture<?> dep = fun.apply(src);
+            checkCompletedWithWrappedException(dep, ex);
+            assertSame(resultOf(src), resultOf(dep));
+        }
+
+        for (Function<CompletableFuture<Integer>, CompletableFuture<?>>
+                 fun : funs) {
+            CompletableFuture<Integer> f = new CompletableFuture<>();
+            CompletableFuture<Integer> src = m.thenApply(f, new IncFunction(m));
+            CompletableFuture<?> dep = fun.apply(src);
+            f.completeExceptionally(ex);
+            checkCompletedWithWrappedException(src, ex);
+            checkCompletedWithWrappedException(dep, ex);
+            assertSame(resultOf(src), resultOf(dep));
+        }
+
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+        for (Function<CompletableFuture<Integer>, CompletableFuture<?>>
+                 fun : funs) {
+            CompletableFuture<Integer> f = new CompletableFuture<>();
+            f.cancel(mayInterruptIfRunning);
+            checkCancelled(f);
+            CompletableFuture<Integer> src = m.thenApply(f, new IncFunction(m));
+            checkCompletedWithWrappedCancellationException(src);
+            CompletableFuture<?> dep = fun.apply(src);
+            checkCompletedWithWrappedCancellationException(dep);
+            assertSame(resultOf(src), resultOf(dep));
+        }
+
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+        for (Function<CompletableFuture<Integer>, CompletableFuture<?>>
+                 fun : funs) {
+            CompletableFuture<Integer> f = new CompletableFuture<>();
+            CompletableFuture<Integer> src = m.thenApply(f, new IncFunction(m));
+            CompletableFuture<?> dep = fun.apply(src);
+            f.cancel(mayInterruptIfRunning);
+            checkCancelled(f);
+            checkCompletedWithWrappedCancellationException(src);
+            checkCompletedWithWrappedCancellationException(dep);
+            assertSame(resultOf(src), resultOf(dep));
+        }
+    }}
+
+    /**
+     * Minimal completion stages throw UOE for all non-CompletionStage methods
+     */
+    public void testMinimalCompletionStage_minimality() {
+        if (!testImplementationDetails) return;
+        Function<Method, String> toSignature =
+            (method) -> method.getName() + Arrays.toString(method.getParameterTypes());
+        Predicate<Method> isNotStatic =
+            (method) -> (method.getModifiers() & Modifier.STATIC) == 0;
+        List<Method> minimalMethods =
+            Stream.of(Object.class, CompletionStage.class)
+            .flatMap((klazz) -> Stream.of(klazz.getMethods()))
+            .filter(isNotStatic)
+            .collect(Collectors.toList());
+        // Methods from CompletableFuture permitted NOT to throw UOE
+        String[] signatureWhitelist = {
+            "newIncompleteFuture[]",
+            "defaultExecutor[]",
+            "minimalCompletionStage[]",
+            "copy[]",
+        };
+        Set<String> permittedMethodSignatures =
+            Stream.concat(minimalMethods.stream().map(toSignature),
+                          Stream.of(signatureWhitelist))
+            .collect(Collectors.toSet());
+        List<Method> allMethods = Stream.of(CompletableFuture.class.getMethods())
+            .filter(isNotStatic)
+            .filter((method) -> !permittedMethodSignatures.contains(toSignature.apply(method)))
+            .collect(Collectors.toList());
+
+        CompletionStage<Integer> minimalStage =
+            new CompletableFuture<Integer>().minimalCompletionStage();
+
+        List<Method> bugs = new ArrayList<>();
+        for (Method method : allMethods) {
+            Class<?>[] parameterTypes = method.getParameterTypes();
+            Object[] args = new Object[parameterTypes.length];
+            // Manufacture boxed primitives for primitive params
+            for (int i = 0; i < args.length; i++) {
+                Class<?> type = parameterTypes[i];
+                if (parameterTypes[i] == boolean.class)
+                    args[i] = false;
+                else if (parameterTypes[i] == int.class)
+                    args[i] = 0;
+                else if (parameterTypes[i] == long.class)
+                    args[i] = 0L;
+            }
+            try {
+                method.invoke(minimalStage, args);
+                bugs.add(method);
+            }
+            catch (java.lang.reflect.InvocationTargetException expected) {
+                if (! (expected.getCause() instanceof UnsupportedOperationException)) {
+                    bugs.add(method);
+                    // expected.getCause().printStackTrace();
+                }
+            }
+            catch (ReflectiveOperationException bad) { throw new Error(bad); }
+        }
+        if (!bugs.isEmpty())
+            throw new Error("Methods did not throw UOE: " + bugs.toString());
+    }
+
+    static class Monad {
+        static class ZeroException extends RuntimeException {
+            public ZeroException() { super("monadic zero"); }
+        }
+        // "return", "unit"
+        static <T> CompletableFuture<T> unit(T value) {
+            return completedFuture(value);
+        }
+        // monadic zero ?
+        static <T> CompletableFuture<T> zero() {
+            return failedFuture(new ZeroException());
+        }
+        // >=>
+        static <T,U,V> Function<T, CompletableFuture<V>> compose
+            (Function<T, CompletableFuture<U>> f,
+             Function<U, CompletableFuture<V>> g) {
+            return (x) -> f.apply(x).thenCompose(g);
+        }
+
+        static void assertZero(CompletableFuture<?> f) {
+            try {
+                f.getNow(null);
+                throw new AssertionFailedError("should throw");
+            } catch (CompletionException success) {
+                assertTrue(success.getCause() instanceof ZeroException);
+            }
+        }
+
+        static <T> void assertFutureEquals(CompletableFuture<T> f,
+                                           CompletableFuture<T> g) {
+            T fval = null, gval = null;
+            Throwable fex = null, gex = null;
+
+            try { fval = f.get(); }
+            catch (ExecutionException ex) { fex = ex.getCause(); }
+            catch (Throwable ex) { fex = ex; }
+
+            try { gval = g.get(); }
+            catch (ExecutionException ex) { gex = ex.getCause(); }
+            catch (Throwable ex) { gex = ex; }
+
+            if (fex != null || gex != null)
+                assertSame(fex.getClass(), gex.getClass());
+            else
+                assertEquals(fval, gval);
+        }
+
+        static class PlusFuture<T> extends CompletableFuture<T> {
+            AtomicReference<Throwable> firstFailure = new AtomicReference<>(null);
+        }
+
+        /** Implements "monadic plus". */
+        static <T> CompletableFuture<T> plus(CompletableFuture<? extends T> f,
+                                             CompletableFuture<? extends T> g) {
+            PlusFuture<T> plus = new PlusFuture<T>();
+            BiConsumer<T, Throwable> action = (T result, Throwable ex) -> {
+                try {
+                    if (ex == null) {
+                        if (plus.complete(result))
+                            if (plus.firstFailure.get() != null)
+                                plus.firstFailure.set(null);
+                    }
+                    else if (plus.firstFailure.compareAndSet(null, ex)) {
+                        if (plus.isDone())
+                            plus.firstFailure.set(null);
+                    }
+                    else {
+                        // first failure has precedence
+                        Throwable first = plus.firstFailure.getAndSet(null);
+
+                        // may fail with "Self-suppression not permitted"
+                        try { first.addSuppressed(ex); }
+                        catch (Exception ignored) {}
+
+                        plus.completeExceptionally(first);
+                    }
+                } catch (Throwable unexpected) {
+                    plus.completeExceptionally(unexpected);
+                }
+            };
+            f.whenComplete(action);
+            g.whenComplete(action);
+            return plus;
+        }
+    }
+
+    /**
+     * CompletableFuture is an additive monad - sort of.
+     * https://en.wikipedia.org/wiki/Monad_(functional_programming)#Additive_monads
+     */
+    public void testAdditiveMonad() throws Throwable {
+        Function<Long, CompletableFuture<Long>> unit = Monad::unit;
+        CompletableFuture<Long> zero = Monad.zero();
+
+        // Some mutually non-commutative functions
+        Function<Long, CompletableFuture<Long>> triple
+            = (x) -> Monad.unit(3 * x);
+        Function<Long, CompletableFuture<Long>> inc
+            = (x) -> Monad.unit(x + 1);
+
+        // unit is a right identity: m >>= unit === m
+        Monad.assertFutureEquals(inc.apply(5L).thenCompose(unit),
+                                 inc.apply(5L));
+        // unit is a left identity: (unit x) >>= f === f x
+        Monad.assertFutureEquals(unit.apply(5L).thenCompose(inc),
+                                 inc.apply(5L));
+
+        // associativity: (m >>= f) >>= g === m >>= ( \x -> (f x >>= g) )
+        Monad.assertFutureEquals(
+            unit.apply(5L).thenCompose(inc).thenCompose(triple),
+            unit.apply(5L).thenCompose((x) -> inc.apply(x).thenCompose(triple)));
+
+        // The case for CompletableFuture as an additive monad is weaker...
+
+        // zero is a monadic zero
+        Monad.assertZero(zero);
+
+        // left zero: zero >>= f === zero
+        Monad.assertZero(zero.thenCompose(inc));
+        // right zero: f >>= (\x -> zero) === zero
+        Monad.assertZero(inc.apply(5L).thenCompose((x) -> zero));
+
+        // f plus zero === f
+        Monad.assertFutureEquals(Monad.unit(5L),
+                                 Monad.plus(Monad.unit(5L), zero));
+        // zero plus f === f
+        Monad.assertFutureEquals(Monad.unit(5L),
+                                 Monad.plus(zero, Monad.unit(5L)));
+        // zero plus zero === zero
+        Monad.assertZero(Monad.plus(zero, zero));
+        {
+            CompletableFuture<Long> f = Monad.plus(Monad.unit(5L),
+                                                   Monad.unit(8L));
+            // non-determinism
+            assertTrue(f.get() == 5L || f.get() == 8L);
+        }
+
+        CompletableFuture<Long> godot = new CompletableFuture<>();
+        // f plus godot === f (doesn't wait for godot)
+        Monad.assertFutureEquals(Monad.unit(5L),
+                                 Monad.plus(Monad.unit(5L), godot));
+        // godot plus f === f (doesn't wait for godot)
+        Monad.assertFutureEquals(Monad.unit(5L),
+                                 Monad.plus(godot, Monad.unit(5L)));
+    }
+
+//     static <U> U join(CompletionStage<U> stage) {
+//         CompletableFuture<U> f = new CompletableFuture<>();
+//         stage.whenComplete((v, ex) -> {
+//             if (ex != null) f.completeExceptionally(ex); else f.complete(v);
+//         });
+//         return f.join();
+//     }
+
+//     static <U> boolean isDone(CompletionStage<U> stage) {
+//         CompletableFuture<U> f = new CompletableFuture<>();
+//         stage.whenComplete((v, ex) -> {
+//             if (ex != null) f.completeExceptionally(ex); else f.complete(v);
+//         });
+//         return f.isDone();
+//     }
+
+//     static <U> U join2(CompletionStage<U> stage) {
+//         return stage.toCompletableFuture().copy().join();
+//     }
+
+//     static <U> boolean isDone2(CompletionStage<U> stage) {
+//         return stage.toCompletableFuture().copy().isDone();
+//     }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/ConcurrentHashMap8Test.java b/jdk/test/java/util/concurrent/tck/ConcurrentHashMap8Test.java
new file mode 100644
index 00000000000..518fab33170
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/ConcurrentHashMap8Test.java
@@ -0,0 +1,1118 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import static java.util.Spliterator.CONCURRENT;
+import static java.util.Spliterator.DISTINCT;
+import static java.util.Spliterator.NONNULL;
+
+import java.util.AbstractMap;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.Spliterator;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.LongAdder;
+import java.util.function.BiFunction;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ConcurrentHashMap8Test extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ConcurrentHashMap8Test.class);
+    }
+
+    /**
+     * Returns a new map from Integers 1-5 to Strings "A"-"E".
+     */
+    private static ConcurrentHashMap map5() {
+        ConcurrentHashMap map = new ConcurrentHashMap(5);
+        assertTrue(map.isEmpty());
+        map.put(one, "A");
+        map.put(two, "B");
+        map.put(three, "C");
+        map.put(four, "D");
+        map.put(five, "E");
+        assertFalse(map.isEmpty());
+        assertEquals(5, map.size());
+        return map;
+    }
+
+    /**
+     * getOrDefault returns value if present, else default
+     */
+    public void testGetOrDefault() {
+        ConcurrentHashMap map = map5();
+        assertEquals(map.getOrDefault(one, "Z"), "A");
+        assertEquals(map.getOrDefault(six, "Z"), "Z");
+    }
+
+    /**
+     * computeIfAbsent adds when the given key is not present
+     */
+    public void testComputeIfAbsent() {
+        ConcurrentHashMap map = map5();
+        map.computeIfAbsent(six, (x) -> "Z");
+        assertTrue(map.containsKey(six));
+    }
+
+    /**
+     * computeIfAbsent does not replace if the key is already present
+     */
+    public void testComputeIfAbsent2() {
+        ConcurrentHashMap map = map5();
+        assertEquals("A", map.computeIfAbsent(one, (x) -> "Z"));
+    }
+
+    /**
+     * computeIfAbsent does not add if function returns null
+     */
+    public void testComputeIfAbsent3() {
+        ConcurrentHashMap map = map5();
+        map.computeIfAbsent(six, (x) -> null);
+        assertFalse(map.containsKey(six));
+    }
+
+    /**
+     * computeIfPresent does not replace if the key is already present
+     */
+    public void testComputeIfPresent() {
+        ConcurrentHashMap map = map5();
+        map.computeIfPresent(six, (x, y) -> "Z");
+        assertFalse(map.containsKey(six));
+    }
+
+    /**
+     * computeIfPresent adds when the given key is not present
+     */
+    public void testComputeIfPresent2() {
+        ConcurrentHashMap map = map5();
+        assertEquals("Z", map.computeIfPresent(one, (x, y) -> "Z"));
+    }
+
+    /**
+     * compute does not replace if the function returns null
+     */
+    public void testCompute() {
+        ConcurrentHashMap map = map5();
+        map.compute(six, (x, y) -> null);
+        assertFalse(map.containsKey(six));
+    }
+
+    /**
+     * compute adds when the given key is not present
+     */
+    public void testCompute2() {
+        ConcurrentHashMap map = map5();
+        assertEquals("Z", map.compute(six, (x, y) -> "Z"));
+    }
+
+    /**
+     * compute replaces when the given key is present
+     */
+    public void testCompute3() {
+        ConcurrentHashMap map = map5();
+        assertEquals("Z", map.compute(one, (x, y) -> "Z"));
+    }
+
+    /**
+     * compute removes when the given key is present and function returns null
+     */
+    public void testCompute4() {
+        ConcurrentHashMap map = map5();
+        map.compute(one, (x, y) -> null);
+        assertFalse(map.containsKey(one));
+    }
+
+    /**
+     * merge adds when the given key is not present
+     */
+    public void testMerge1() {
+        ConcurrentHashMap map = map5();
+        assertEquals("Y", map.merge(six, "Y", (x, y) -> "Z"));
+    }
+
+    /**
+     * merge replaces when the given key is present
+     */
+    public void testMerge2() {
+        ConcurrentHashMap map = map5();
+        assertEquals("Z", map.merge(one, "Y", (x, y) -> "Z"));
+    }
+
+    /**
+     * merge removes when the given key is present and function returns null
+     */
+    public void testMerge3() {
+        ConcurrentHashMap map = map5();
+        map.merge(one, "Y", (x, y) -> null);
+        assertFalse(map.containsKey(one));
+    }
+
+    static Set<Integer> populatedSet(int n) {
+        Set<Integer> a = ConcurrentHashMap.<Integer>newKeySet();
+        assertTrue(a.isEmpty());
+        for (int i = 0; i < n; i++)
+            assertTrue(a.add(i));
+        assertEquals(n == 0, a.isEmpty());
+        assertEquals(n, a.size());
+        return a;
+    }
+
+    static Set populatedSet(Integer[] elements) {
+        Set<Integer> a = ConcurrentHashMap.<Integer>newKeySet();
+        assertTrue(a.isEmpty());
+        for (int i = 0; i < elements.length; i++)
+            assertTrue(a.add(elements[i]));
+        assertFalse(a.isEmpty());
+        assertEquals(elements.length, a.size());
+        return a;
+    }
+
+    /**
+     * replaceAll replaces all matching values.
+     */
+    public void testReplaceAll() {
+        ConcurrentHashMap<Integer, String> map = map5();
+        map.replaceAll((x, y) -> { return x > 3 ? "Z" : y; });
+        assertEquals("A", map.get(one));
+        assertEquals("B", map.get(two));
+        assertEquals("C", map.get(three));
+        assertEquals("Z", map.get(four));
+        assertEquals("Z", map.get(five));
+    }
+
+    /**
+     * Default-constructed set is empty
+     */
+    public void testNewKeySet() {
+        Set a = ConcurrentHashMap.newKeySet();
+        assertTrue(a.isEmpty());
+    }
+
+    /**
+     * keySet.add adds the key with the established value to the map;
+     * remove removes it.
+     */
+    public void testKeySetAddRemove() {
+        ConcurrentHashMap map = map5();
+        Set set1 = map.keySet();
+        Set set2 = map.keySet(true);
+        set2.add(six);
+        assertTrue(((ConcurrentHashMap.KeySetView)set2).getMap() == map);
+        assertTrue(((ConcurrentHashMap.KeySetView)set1).getMap() == map);
+        assertEquals(set2.size(), map.size());
+        assertEquals(set1.size(), map.size());
+        assertTrue((Boolean)map.get(six));
+        assertTrue(set1.contains(six));
+        assertTrue(set2.contains(six));
+        set2.remove(six);
+        assertNull(map.get(six));
+        assertFalse(set1.contains(six));
+        assertFalse(set2.contains(six));
+    }
+
+    /**
+     * keySet.addAll adds each element from the given collection
+     */
+    public void testAddAll() {
+        Set full = populatedSet(3);
+        assertTrue(full.addAll(Arrays.asList(three, four, five)));
+        assertEquals(6, full.size());
+        assertFalse(full.addAll(Arrays.asList(three, four, five)));
+        assertEquals(6, full.size());
+    }
+
+    /**
+     * keySet.addAll adds each element from the given collection that did not
+     * already exist in the set
+     */
+    public void testAddAll2() {
+        Set full = populatedSet(3);
+        // "one" is duplicate and will not be added
+        assertTrue(full.addAll(Arrays.asList(three, four, one)));
+        assertEquals(5, full.size());
+        assertFalse(full.addAll(Arrays.asList(three, four, one)));
+        assertEquals(5, full.size());
+    }
+
+    /**
+     * keySet.add will not add the element if it already exists in the set
+     */
+    public void testAdd2() {
+        Set full = populatedSet(3);
+        assertFalse(full.add(one));
+        assertEquals(3, full.size());
+    }
+
+    /**
+     * keySet.add adds the element when it does not exist in the set
+     */
+    public void testAdd3() {
+        Set full = populatedSet(3);
+        assertTrue(full.add(three));
+        assertTrue(full.contains(three));
+        assertFalse(full.add(three));
+        assertTrue(full.contains(three));
+    }
+
+    /**
+     * keySet.add throws UnsupportedOperationException if no default
+     * mapped value
+     */
+    public void testAdd4() {
+        Set full = map5().keySet();
+        try {
+            full.add(three);
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+    }
+
+    /**
+     * keySet.add throws NullPointerException if the specified key is
+     * null
+     */
+    public void testAdd5() {
+        Set full = populatedSet(3);
+        try {
+            full.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * KeySetView.getMappedValue returns the map's mapped value
+     */
+    public void testGetMappedValue() {
+        ConcurrentHashMap map = map5();
+        assertNull(map.keySet().getMappedValue());
+        try {
+            map.keySet(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+        ConcurrentHashMap.KeySetView set = map.keySet(one);
+        assertFalse(set.add(one));
+        assertTrue(set.add(six));
+        assertTrue(set.add(seven));
+        assertTrue(set.getMappedValue() == one);
+        assertTrue(map.get(one) != one);
+        assertTrue(map.get(six) == one);
+        assertTrue(map.get(seven) == one);
+    }
+
+    void checkSpliteratorCharacteristics(Spliterator<?> sp,
+                                         int requiredCharacteristics) {
+        assertEquals(requiredCharacteristics,
+                     requiredCharacteristics & sp.characteristics());
+    }
+
+    /**
+     * KeySetView.spliterator returns spliterator over the elements in this set
+     */
+    public void testKeySetSpliterator() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap map = map5();
+        Set set = map.keySet();
+        Spliterator<Integer> sp = set.spliterator();
+        checkSpliteratorCharacteristics(sp, CONCURRENT | DISTINCT | NONNULL);
+        assertEquals(sp.estimateSize(), map.size());
+        Spliterator<Integer> sp2 = sp.trySplit();
+        sp.forEachRemaining((Integer x) -> adder.add(x.longValue()));
+        long v = adder.sumThenReset();
+        sp2.forEachRemaining((Integer x) -> adder.add(x.longValue()));
+        long v2 = adder.sum();
+        assertEquals(v + v2, 15);
+    }
+
+    /**
+     * keyset.clear removes all elements from the set
+     */
+    public void testClear() {
+        Set full = populatedSet(3);
+        full.clear();
+        assertEquals(0, full.size());
+    }
+
+    /**
+     * keyset.contains returns true for added elements
+     */
+    public void testContains() {
+        Set full = populatedSet(3);
+        assertTrue(full.contains(one));
+        assertFalse(full.contains(five));
+    }
+
+    /**
+     * KeySets with equal elements are equal
+     */
+    public void testEquals() {
+        Set a = populatedSet(3);
+        Set b = populatedSet(3);
+        assertTrue(a.equals(b));
+        assertTrue(b.equals(a));
+        assertEquals(a.hashCode(), b.hashCode());
+        a.add(m1);
+        assertFalse(a.equals(b));
+        assertFalse(b.equals(a));
+        b.add(m1);
+        assertTrue(a.equals(b));
+        assertTrue(b.equals(a));
+        assertEquals(a.hashCode(), b.hashCode());
+    }
+
+    /**
+     * KeySet.containsAll returns true for collections with subset of elements
+     */
+    public void testContainsAll() {
+        Collection full = populatedSet(3);
+        assertTrue(full.containsAll(Arrays.asList()));
+        assertTrue(full.containsAll(Arrays.asList(one)));
+        assertTrue(full.containsAll(Arrays.asList(one, two)));
+        assertFalse(full.containsAll(Arrays.asList(one, two, six)));
+        assertFalse(full.containsAll(Arrays.asList(six)));
+    }
+
+    /**
+     * KeySet.isEmpty is true when empty, else false
+     */
+    public void testIsEmpty() {
+        assertTrue(populatedSet(0).isEmpty());
+        assertFalse(populatedSet(3).isEmpty());
+    }
+
+    /**
+     * KeySet.iterator() returns an iterator containing the elements of the
+     * set
+     */
+    public void testIterator() {
+        Collection empty = ConcurrentHashMap.newKeySet();
+        int size = 20;
+        assertFalse(empty.iterator().hasNext());
+        try {
+            empty.iterator().next();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+
+        Integer[] elements = new Integer[size];
+        for (int i = 0; i < size; i++)
+            elements[i] = i;
+        Collections.shuffle(Arrays.asList(elements));
+        Collection<Integer> full = populatedSet(elements);
+
+        Iterator it = full.iterator();
+        for (int j = 0; j < size; j++) {
+            assertTrue(it.hasNext());
+            it.next();
+        }
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty collections has no elements
+     */
+    public void testEmptyIterator() {
+        assertIteratorExhausted(ConcurrentHashMap.newKeySet().iterator());
+        assertIteratorExhausted(new ConcurrentHashMap().entrySet().iterator());
+        assertIteratorExhausted(new ConcurrentHashMap().values().iterator());
+        assertIteratorExhausted(new ConcurrentHashMap().keySet().iterator());
+    }
+
+    /**
+     * KeySet.iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        Set q = populatedSet(3);
+        Iterator it = q.iterator();
+        Object removed = it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertFalse(it.next().equals(removed));
+        assertFalse(it.next().equals(removed));
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * KeySet.toString holds toString of elements
+     */
+    public void testToString() {
+        assertEquals("[]", ConcurrentHashMap.newKeySet().toString());
+        Set full = populatedSet(3);
+        String s = full.toString();
+        for (int i = 0; i < 3; ++i)
+            assertTrue(s.contains(String.valueOf(i)));
+    }
+
+    /**
+     * KeySet.removeAll removes all elements from the given collection
+     */
+    public void testRemoveAll() {
+        Set full = populatedSet(3);
+        assertTrue(full.removeAll(Arrays.asList(one, two)));
+        assertEquals(1, full.size());
+        assertFalse(full.removeAll(Arrays.asList(one, two)));
+        assertEquals(1, full.size());
+    }
+
+    /**
+     * KeySet.remove removes an element
+     */
+    public void testRemove() {
+        Set full = populatedSet(3);
+        full.remove(one);
+        assertFalse(full.contains(one));
+        assertEquals(2, full.size());
+    }
+
+    /**
+     * keySet.size returns the number of elements
+     */
+    public void testSize() {
+        Set empty = ConcurrentHashMap.newKeySet();
+        Set full = populatedSet(3);
+        assertEquals(3, full.size());
+        assertEquals(0, empty.size());
+    }
+
+    /**
+     * KeySet.toArray() returns an Object array containing all elements from
+     * the set
+     */
+    public void testToArray() {
+        Object[] a = ConcurrentHashMap.newKeySet().toArray();
+        assertTrue(Arrays.equals(new Object[0], a));
+        assertSame(Object[].class, a.getClass());
+        int size = 20;
+        Integer[] elements = new Integer[size];
+        for (int i = 0; i < size; i++)
+            elements[i] = i;
+        Collections.shuffle(Arrays.asList(elements));
+        Collection<Integer> full = populatedSet(elements);
+
+        assertTrue(Arrays.asList(elements).containsAll(Arrays.asList(full.toArray())));
+        assertTrue(full.containsAll(Arrays.asList(full.toArray())));
+        assertSame(Object[].class, full.toArray().getClass());
+    }
+
+    /**
+     * toArray(Integer array) returns an Integer array containing all
+     * elements from the set
+     */
+    public void testToArray2() {
+        Collection empty = ConcurrentHashMap.newKeySet();
+        Integer[] a;
+        int size = 20;
+
+        a = new Integer[0];
+        assertSame(a, empty.toArray(a));
+
+        a = new Integer[size / 2];
+        Arrays.fill(a, 42);
+        assertSame(a, empty.toArray(a));
+        assertNull(a[0]);
+        for (int i = 1; i < a.length; i++)
+            assertEquals(42, (int) a[i]);
+
+        Integer[] elements = new Integer[size];
+        for (int i = 0; i < size; i++)
+            elements[i] = i;
+        Collections.shuffle(Arrays.asList(elements));
+        Collection<Integer> full = populatedSet(elements);
+
+        Arrays.fill(a, 42);
+        assertTrue(Arrays.asList(elements).containsAll(Arrays.asList(full.toArray(a))));
+        for (int i = 0; i < a.length; i++)
+            assertEquals(42, (int) a[i]);
+        assertSame(Integer[].class, full.toArray(a).getClass());
+
+        a = new Integer[size];
+        Arrays.fill(a, 42);
+        assertSame(a, full.toArray(a));
+        assertTrue(Arrays.asList(elements).containsAll(Arrays.asList(full.toArray(a))));
+    }
+
+    /**
+     * A deserialized serialized set is equal
+     */
+    public void testSerialization() throws Exception {
+        int size = 20;
+        Set x = populatedSet(size);
+        Set y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    static final int SIZE = 10000;
+    static ConcurrentHashMap<Long, Long> longMap;
+
+    static ConcurrentHashMap<Long, Long> longMap() {
+        if (longMap == null) {
+            longMap = new ConcurrentHashMap<Long, Long>(SIZE);
+            for (int i = 0; i < SIZE; ++i)
+                longMap.put(Long.valueOf(i), Long.valueOf(2 *i));
+        }
+        return longMap;
+    }
+
+    // explicit function class to avoid type inference problems
+    static class AddKeys implements BiFunction<Map.Entry<Long,Long>, Map.Entry<Long,Long>, Map.Entry<Long,Long>> {
+        public Map.Entry<Long,Long> apply(Map.Entry<Long,Long> x, Map.Entry<Long,Long> y) {
+            return new AbstractMap.SimpleEntry<Long,Long>
+             (Long.valueOf(x.getKey().longValue() + y.getKey().longValue()),
+              Long.valueOf(1L));
+        }
+    }
+
+    /**
+     * forEachKeySequentially traverses all keys
+     */
+    public void testForEachKeySequentially() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEachKey(Long.MAX_VALUE, (Long x) -> adder.add(x.longValue()));
+        assertEquals(adder.sum(), SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * forEachValueSequentially traverses all values
+     */
+    public void testForEachValueSequentially() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEachValue(Long.MAX_VALUE, (Long x) -> adder.add(x.longValue()));
+        assertEquals(adder.sum(), SIZE * (SIZE - 1));
+    }
+
+    /**
+     * forEachSequentially traverses all mappings
+     */
+    public void testForEachSequentially() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEach(Long.MAX_VALUE, (Long x, Long y) -> adder.add(x.longValue() + y.longValue()));
+        assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * forEachEntrySequentially traverses all entries
+     */
+    public void testForEachEntrySequentially() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEachEntry(Long.MAX_VALUE, (Map.Entry<Long,Long> e) -> adder.add(e.getKey().longValue() + e.getValue().longValue()));
+        assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * forEachKeyInParallel traverses all keys
+     */
+    public void testForEachKeyInParallel() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEachKey(1L, (Long x) -> adder.add(x.longValue()));
+        assertEquals(adder.sum(), SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * forEachValueInParallel traverses all values
+     */
+    public void testForEachValueInParallel() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEachValue(1L, (Long x) -> adder.add(x.longValue()));
+        assertEquals(adder.sum(), SIZE * (SIZE - 1));
+    }
+
+    /**
+     * forEachInParallel traverses all mappings
+     */
+    public void testForEachInParallel() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEach(1L, (Long x, Long y) -> adder.add(x.longValue() + y.longValue()));
+        assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * forEachEntryInParallel traverses all entries
+     */
+    public void testForEachEntryInParallel() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEachEntry(1L, (Map.Entry<Long,Long> e) -> adder.add(e.getKey().longValue() + e.getValue().longValue()));
+        assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * Mapped forEachKeySequentially traverses the given
+     * transformations of all keys
+     */
+    public void testMappedForEachKeySequentially() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEachKey(Long.MAX_VALUE, (Long x) -> Long.valueOf(4 * x.longValue()),
+                                 (Long x) -> adder.add(x.longValue()));
+        assertEquals(adder.sum(), 4 * SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * Mapped forEachValueSequentially traverses the given
+     * transformations of all values
+     */
+    public void testMappedForEachValueSequentially() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEachValue(Long.MAX_VALUE, (Long x) -> Long.valueOf(4 * x.longValue()),
+                                   (Long x) -> adder.add(x.longValue()));
+        assertEquals(adder.sum(), 4 * SIZE * (SIZE - 1));
+    }
+
+    /**
+     * Mapped forEachSequentially traverses the given
+     * transformations of all mappings
+     */
+    public void testMappedForEachSequentially() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEach(Long.MAX_VALUE, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()),
+                              (Long x) -> adder.add(x.longValue()));
+        assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * Mapped forEachEntrySequentially traverses the given
+     * transformations of all entries
+     */
+    public void testMappedForEachEntrySequentially() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEachEntry(Long.MAX_VALUE, (Map.Entry<Long,Long> e) -> Long.valueOf(e.getKey().longValue() + e.getValue().longValue()),
+                                   (Long x) -> adder.add(x.longValue()));
+        assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * Mapped forEachKeyInParallel traverses the given
+     * transformations of all keys
+     */
+    public void testMappedForEachKeyInParallel() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEachKey(1L, (Long x) -> Long.valueOf(4 * x.longValue()),
+                               (Long x) -> adder.add(x.longValue()));
+        assertEquals(adder.sum(), 4 * SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * Mapped forEachValueInParallel traverses the given
+     * transformations of all values
+     */
+    public void testMappedForEachValueInParallel() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEachValue(1L, (Long x) -> Long.valueOf(4 * x.longValue()),
+                                 (Long x) -> adder.add(x.longValue()));
+        assertEquals(adder.sum(), 4 * SIZE * (SIZE - 1));
+    }
+
+    /**
+     * Mapped forEachInParallel traverses the given
+     * transformations of all mappings
+     */
+    public void testMappedForEachInParallel() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEach(1L, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()),
+                            (Long x) -> adder.add(x.longValue()));
+        assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * Mapped forEachEntryInParallel traverses the given
+     * transformations of all entries
+     */
+    public void testMappedForEachEntryInParallel() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEachEntry(1L, (Map.Entry<Long,Long> e) -> Long.valueOf(e.getKey().longValue() + e.getValue().longValue()),
+                                 (Long x) -> adder.add(x.longValue()));
+        assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * reduceKeysSequentially accumulates across all keys,
+     */
+    public void testReduceKeysSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r;
+        r = m.reduceKeys(Long.MAX_VALUE, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+        assertEquals((long)r, (long)SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * reduceValuesSequentially accumulates across all values
+     */
+    public void testReduceValuesSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r;
+        r = m.reduceKeys(Long.MAX_VALUE, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+        assertEquals((long)r, (long)SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * reduceEntriesSequentially accumulates across all entries
+     */
+    public void testReduceEntriesSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Map.Entry<Long,Long> r;
+        r = m.reduceEntries(Long.MAX_VALUE, new AddKeys());
+        assertEquals(r.getKey().longValue(), (long)SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * reduceKeysInParallel accumulates across all keys
+     */
+    public void testReduceKeysInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r;
+        r = m.reduceKeys(1L, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+        assertEquals((long)r, (long)SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * reduceValuesInParallel accumulates across all values
+     */
+    public void testReduceValuesInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r;
+        r = m.reduceValues(1L, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+        assertEquals((long)r, (long)SIZE * (SIZE - 1));
+    }
+
+    /**
+     * reduceEntriesInParallel accumulate across all entries
+     */
+    public void testReduceEntriesInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Map.Entry<Long,Long> r;
+        r = m.reduceEntries(1L, new AddKeys());
+        assertEquals(r.getKey().longValue(), (long)SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * Mapped reduceKeysSequentially accumulates mapped keys
+     */
+    public void testMapReduceKeysSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r = m.reduceKeys(Long.MAX_VALUE, (Long x) -> Long.valueOf(4 * x.longValue()),
+                                     (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+        assertEquals((long)r, (long)4 * SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * Mapped reduceValuesSequentially accumulates mapped values
+     */
+    public void testMapReduceValuesSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r = m.reduceValues(Long.MAX_VALUE, (Long x) -> Long.valueOf(4 * x.longValue()),
+                                       (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+        assertEquals((long)r, (long)4 * SIZE * (SIZE - 1));
+    }
+
+    /**
+     * reduceSequentially accumulates across all transformed mappings
+     */
+    public void testMappedReduceSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r = m.reduce(Long.MAX_VALUE, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()),
+                                 (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+
+        assertEquals((long)r, (long)3 * SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * Mapped reduceKeysInParallel, accumulates mapped keys
+     */
+    public void testMapReduceKeysInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r = m.reduceKeys(1L, (Long x) -> Long.valueOf(4 * x.longValue()),
+                                   (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+        assertEquals((long)r, (long)4 * SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * Mapped reduceValuesInParallel accumulates mapped values
+     */
+    public void testMapReduceValuesInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r = m.reduceValues(1L, (Long x) -> Long.valueOf(4 * x.longValue()),
+                                     (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+        assertEquals((long)r, (long)4 * SIZE * (SIZE - 1));
+    }
+
+    /**
+     * reduceInParallel accumulate across all transformed mappings
+     */
+    public void testMappedReduceInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r;
+        r = m.reduce(1L, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()),
+                               (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+        assertEquals((long)r, (long)3 * SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * reduceKeysToLongSequentially accumulates mapped keys
+     */
+    public void testReduceKeysToLongSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        long lr = m.reduceKeysToLong(Long.MAX_VALUE, (Long x) -> x.longValue(), 0L, Long::sum);
+        assertEquals(lr, (long)SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * reduceKeysToIntSequentially accumulates mapped keys
+     */
+    public void testReduceKeysToIntSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        int ir = m.reduceKeysToInt(Long.MAX_VALUE, (Long x) -> x.intValue(), 0, Integer::sum);
+        assertEquals(ir, SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * reduceKeysToDoubleSequentially accumulates mapped keys
+     */
+    public void testReduceKeysToDoubleSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        double dr = m.reduceKeysToDouble(Long.MAX_VALUE, (Long x) -> x.doubleValue(), 0.0, Double::sum);
+        assertEquals(dr, (double)SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * reduceValuesToLongSequentially accumulates mapped values
+     */
+    public void testReduceValuesToLongSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        long lr = m.reduceValuesToLong(Long.MAX_VALUE, (Long x) -> x.longValue(), 0L, Long::sum);
+        assertEquals(lr, (long)SIZE * (SIZE - 1));
+    }
+
+    /**
+     * reduceValuesToIntSequentially accumulates mapped values
+     */
+    public void testReduceValuesToIntSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        int ir = m.reduceValuesToInt(Long.MAX_VALUE, (Long x) -> x.intValue(), 0, Integer::sum);
+        assertEquals(ir, SIZE * (SIZE - 1));
+    }
+
+    /**
+     * reduceValuesToDoubleSequentially accumulates mapped values
+     */
+    public void testReduceValuesToDoubleSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        double dr = m.reduceValuesToDouble(Long.MAX_VALUE, (Long x) -> x.doubleValue(), 0.0, Double::sum);
+        assertEquals(dr, (double)SIZE * (SIZE - 1));
+    }
+
+    /**
+     * reduceKeysToLongInParallel accumulates mapped keys
+     */
+    public void testReduceKeysToLongInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        long lr = m.reduceKeysToLong(1L, (Long x) -> x.longValue(), 0L, Long::sum);
+        assertEquals(lr, (long)SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * reduceKeysToIntInParallel accumulates mapped keys
+     */
+    public void testReduceKeysToIntInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        int ir = m.reduceKeysToInt(1L, (Long x) -> x.intValue(), 0, Integer::sum);
+        assertEquals(ir, SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * reduceKeysToDoubleInParallel accumulates mapped values
+     */
+    public void testReduceKeysToDoubleInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        double dr = m.reduceKeysToDouble(1L, (Long x) -> x.doubleValue(), 0.0, Double::sum);
+        assertEquals(dr, (double)SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * reduceValuesToLongInParallel accumulates mapped values
+     */
+    public void testReduceValuesToLongInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        long lr = m.reduceValuesToLong(1L, (Long x) -> x.longValue(), 0L, Long::sum);
+        assertEquals(lr, (long)SIZE * (SIZE - 1));
+    }
+
+    /**
+     * reduceValuesToIntInParallel accumulates mapped values
+     */
+    public void testReduceValuesToIntInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        int ir = m.reduceValuesToInt(1L, (Long x) -> x.intValue(), 0, Integer::sum);
+        assertEquals(ir, SIZE * (SIZE - 1));
+    }
+
+    /**
+     * reduceValuesToDoubleInParallel accumulates mapped values
+     */
+    public void testReduceValuesToDoubleInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        double dr = m.reduceValuesToDouble(1L, (Long x) -> x.doubleValue(), 0.0, Double::sum);
+        assertEquals(dr, (double)SIZE * (SIZE - 1));
+    }
+
+    /**
+     * searchKeysSequentially returns a non-null result of search
+     * function, or null if none
+     */
+    public void testSearchKeysSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r;
+        r = m.searchKeys(Long.MAX_VALUE, (Long x) -> x.longValue() == (long)(SIZE/2) ? x : null);
+        assertEquals((long)r, (long)(SIZE/2));
+        r = m.searchKeys(Long.MAX_VALUE, (Long x) -> x.longValue() < 0L ? x : null);
+        assertNull(r);
+    }
+
+    /**
+     * searchValuesSequentially returns a non-null result of search
+     * function, or null if none
+     */
+    public void testSearchValuesSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r;
+        r = m.searchValues(Long.MAX_VALUE,
+            (Long x) -> (x.longValue() == (long)(SIZE/2)) ? x : null);
+        assertEquals((long)r, (long)(SIZE/2));
+        r = m.searchValues(Long.MAX_VALUE,
+            (Long x) -> (x.longValue() < 0L) ? x : null);
+        assertNull(r);
+    }
+
+    /**
+     * searchSequentially returns a non-null result of search
+     * function, or null if none
+     */
+    public void testSearchSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r;
+        r = m.search(Long.MAX_VALUE, (Long x, Long y) -> x.longValue() == (long)(SIZE/2) ? x : null);
+        assertEquals((long)r, (long)(SIZE/2));
+        r = m.search(Long.MAX_VALUE, (Long x, Long y) -> x.longValue() < 0L ? x : null);
+        assertNull(r);
+    }
+
+    /**
+     * searchEntriesSequentially returns a non-null result of search
+     * function, or null if none
+     */
+    public void testSearchEntriesSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r;
+        r = m.searchEntries(Long.MAX_VALUE, (Map.Entry<Long,Long> e) -> e.getKey().longValue() == (long)(SIZE/2) ? e.getKey() : null);
+        assertEquals((long)r, (long)(SIZE/2));
+        r = m.searchEntries(Long.MAX_VALUE, (Map.Entry<Long,Long> e) -> e.getKey().longValue() < 0L ? e.getKey() : null);
+        assertNull(r);
+    }
+
+    /**
+     * searchKeysInParallel returns a non-null result of search
+     * function, or null if none
+     */
+    public void testSearchKeysInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r;
+        r = m.searchKeys(1L, (Long x) -> x.longValue() == (long)(SIZE/2) ? x : null);
+        assertEquals((long)r, (long)(SIZE/2));
+        r = m.searchKeys(1L, (Long x) -> x.longValue() < 0L ? x : null);
+        assertNull(r);
+    }
+
+    /**
+     * searchValuesInParallel returns a non-null result of search
+     * function, or null if none
+     */
+    public void testSearchValuesInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r;
+        r = m.searchValues(1L, (Long x) -> x.longValue() == (long)(SIZE/2) ? x : null);
+        assertEquals((long)r, (long)(SIZE/2));
+        r = m.searchValues(1L, (Long x) -> x.longValue() < 0L ? x : null);
+        assertNull(r);
+    }
+
+    /**
+     * searchInParallel returns a non-null result of search function,
+     * or null if none
+     */
+    public void testSearchInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r;
+        r = m.search(1L, (Long x, Long y) -> x.longValue() == (long)(SIZE/2) ? x : null);
+        assertEquals((long)r, (long)(SIZE/2));
+        r = m.search(1L, (Long x, Long y) -> x.longValue() < 0L ? x : null);
+        assertNull(r);
+    }
+
+    /**
+     * searchEntriesInParallel returns a non-null result of search
+     * function, or null if none
+     */
+    public void testSearchEntriesInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r;
+        r = m.searchEntries(1L, (Map.Entry<Long,Long> e) -> e.getKey().longValue() == (long)(SIZE/2) ? e.getKey() : null);
+        assertEquals((long)r, (long)(SIZE/2));
+        r = m.searchEntries(1L, (Map.Entry<Long,Long> e) -> e.getKey().longValue() < 0L ? e.getKey() : null);
+        assertNull(r);
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/ConcurrentHashMapTest.java b/jdk/test/java/util/concurrent/tck/ConcurrentHashMapTest.java
new file mode 100644
index 00000000000..766511902af
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/ConcurrentHashMapTest.java
@@ -0,0 +1,833 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ConcurrentHashMapTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ConcurrentHashMapTest.class);
+    }
+
+    /**
+     * Returns a new map from Integers 1-5 to Strings "A"-"E".
+     */
+    private static ConcurrentHashMap<Integer, String> map5() {
+        ConcurrentHashMap map = new ConcurrentHashMap<Integer, String>(5);
+        assertTrue(map.isEmpty());
+        map.put(one, "A");
+        map.put(two, "B");
+        map.put(three, "C");
+        map.put(four, "D");
+        map.put(five, "E");
+        assertFalse(map.isEmpty());
+        assertEquals(5, map.size());
+        return map;
+    }
+
+    /** Re-implement Integer.compare for old java versions */
+    static int compare(int x, int y) {
+        return (x < y) ? -1 : (x > y) ? 1 : 0;
+    }
+
+    // classes for testing Comparable fallbacks
+    static class BI implements Comparable<BI> {
+        private final int value;
+        BI(int value) { this.value = value; }
+        public int compareTo(BI other) {
+            return compare(value, other.value);
+        }
+        public boolean equals(Object x) {
+            return (x instanceof BI) && ((BI)x).value == value;
+        }
+        public int hashCode() { return 42; }
+    }
+    static class CI extends BI { CI(int value) { super(value); } }
+    static class DI extends BI { DI(int value) { super(value); } }
+
+    static class BS implements Comparable<BS> {
+        private final String value;
+        BS(String value) { this.value = value; }
+        public int compareTo(BS other) {
+            return value.compareTo(other.value);
+        }
+        public boolean equals(Object x) {
+            return (x instanceof BS) && value.equals(((BS)x).value);
+        }
+        public int hashCode() { return 42; }
+    }
+
+    static class LexicographicList<E extends Comparable<E>> extends ArrayList<E>
+        implements Comparable<LexicographicList<E>> {
+        LexicographicList(Collection<E> c) { super(c); }
+        LexicographicList(E e) { super(Collections.singleton(e)); }
+        public int compareTo(LexicographicList<E> other) {
+            int common = Math.min(size(), other.size());
+            int r = 0;
+            for (int i = 0; i < common; i++) {
+                if ((r = get(i).compareTo(other.get(i))) != 0)
+                    break;
+            }
+            if (r == 0)
+                r = compare(size(), other.size());
+            return r;
+        }
+        private static final long serialVersionUID = 0;
+    }
+
+    static class CollidingObject {
+        final String value;
+        CollidingObject(final String value) { this.value = value; }
+        public int hashCode() { return this.value.hashCode() & 1; }
+        public boolean equals(final Object obj) {
+            return (obj instanceof CollidingObject) && ((CollidingObject)obj).value.equals(value);
+        }
+    }
+
+    static class ComparableCollidingObject extends CollidingObject implements Comparable<ComparableCollidingObject> {
+        ComparableCollidingObject(final String value) { super(value); }
+        public int compareTo(final ComparableCollidingObject o) {
+            return value.compareTo(o.value);
+        }
+    }
+
+    /**
+     * Inserted elements that are subclasses of the same Comparable
+     * class are found.
+     */
+    public void testComparableFamily() {
+        int size = 500;         // makes measured test run time -> 60ms
+        ConcurrentHashMap<BI, Boolean> m =
+            new ConcurrentHashMap<BI, Boolean>();
+        for (int i = 0; i < size; i++) {
+            assertTrue(m.put(new CI(i), true) == null);
+        }
+        for (int i = 0; i < size; i++) {
+            assertTrue(m.containsKey(new CI(i)));
+            assertTrue(m.containsKey(new DI(i)));
+        }
+    }
+
+    /**
+     * Elements of classes with erased generic type parameters based
+     * on Comparable can be inserted and found.
+     */
+    public void testGenericComparable() {
+        int size = 120;         // makes measured test run time -> 60ms
+        ConcurrentHashMap<Object, Boolean> m =
+            new ConcurrentHashMap<Object, Boolean>();
+        for (int i = 0; i < size; i++) {
+            BI bi = new BI(i);
+            BS bs = new BS(String.valueOf(i));
+            LexicographicList<BI> bis = new LexicographicList<BI>(bi);
+            LexicographicList<BS> bss = new LexicographicList<BS>(bs);
+            assertTrue(m.putIfAbsent(bis, true) == null);
+            assertTrue(m.containsKey(bis));
+            if (m.putIfAbsent(bss, true) == null)
+                assertTrue(m.containsKey(bss));
+            assertTrue(m.containsKey(bis));
+        }
+        for (int i = 0; i < size; i++) {
+            assertTrue(m.containsKey(Collections.singletonList(new BI(i))));
+        }
+    }
+
+    /**
+     * Elements of non-comparable classes equal to those of classes
+     * with erased generic type parameters based on Comparable can be
+     * inserted and found.
+     */
+    public void testGenericComparable2() {
+        int size = 500;         // makes measured test run time -> 60ms
+        ConcurrentHashMap<Object, Boolean> m =
+            new ConcurrentHashMap<Object, Boolean>();
+        for (int i = 0; i < size; i++) {
+            m.put(Collections.singletonList(new BI(i)), true);
+        }
+
+        for (int i = 0; i < size; i++) {
+            LexicographicList<BI> bis = new LexicographicList<BI>(new BI(i));
+            assertTrue(m.containsKey(bis));
+        }
+    }
+
+    /**
+     * Mixtures of instances of comparable and non-comparable classes
+     * can be inserted and found.
+     */
+    public void testMixedComparable() {
+        int size = 1200;        // makes measured test run time -> 35ms
+        ConcurrentHashMap<Object, Object> map =
+            new ConcurrentHashMap<Object, Object>();
+        Random rng = new Random();
+        for (int i = 0; i < size; i++) {
+            Object x;
+            switch (rng.nextInt(4)) {
+            case 0:
+                x = new Object();
+                break;
+            case 1:
+                x = new CollidingObject(Integer.toString(i));
+                break;
+            default:
+                x = new ComparableCollidingObject(Integer.toString(i));
+            }
+            assertNull(map.put(x, x));
+        }
+        int count = 0;
+        for (Object k : map.keySet()) {
+            assertEquals(map.get(k), k);
+            ++count;
+        }
+        assertEquals(count, size);
+        assertEquals(map.size(), size);
+        for (Object k : map.keySet()) {
+            assertEquals(map.put(k, k), k);
+        }
+    }
+
+    /**
+     * clear removes all pairs
+     */
+    public void testClear() {
+        ConcurrentHashMap map = map5();
+        map.clear();
+        assertEquals(0, map.size());
+    }
+
+    /**
+     * Maps with same contents are equal
+     */
+    public void testEquals() {
+        ConcurrentHashMap map1 = map5();
+        ConcurrentHashMap map2 = map5();
+        assertEquals(map1, map2);
+        assertEquals(map2, map1);
+        map1.clear();
+        assertFalse(map1.equals(map2));
+        assertFalse(map2.equals(map1));
+    }
+
+    /**
+     * hashCode() equals sum of each key.hashCode ^ value.hashCode
+     */
+    public void testHashCode() {
+        ConcurrentHashMap<Integer,String> map = map5();
+        int sum = 0;
+        for (Map.Entry<Integer,String> e : map.entrySet())
+            sum += e.getKey().hashCode() ^ e.getValue().hashCode();
+        assertEquals(sum, map.hashCode());
+    }
+
+    /**
+     * contains returns true for contained value
+     */
+    public void testContains() {
+        ConcurrentHashMap map = map5();
+        assertTrue(map.contains("A"));
+        assertFalse(map.contains("Z"));
+    }
+
+    /**
+     * containsKey returns true for contained key
+     */
+    public void testContainsKey() {
+        ConcurrentHashMap map = map5();
+        assertTrue(map.containsKey(one));
+        assertFalse(map.containsKey(zero));
+    }
+
+    /**
+     * containsValue returns true for held values
+     */
+    public void testContainsValue() {
+        ConcurrentHashMap map = map5();
+        assertTrue(map.containsValue("A"));
+        assertFalse(map.containsValue("Z"));
+    }
+
+    /**
+     * enumeration returns an enumeration containing the correct
+     * elements
+     */
+    public void testEnumeration() {
+        ConcurrentHashMap map = map5();
+        Enumeration e = map.elements();
+        int count = 0;
+        while (e.hasMoreElements()) {
+            count++;
+            e.nextElement();
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * get returns the correct element at the given key,
+     * or null if not present
+     */
+    public void testGet() {
+        ConcurrentHashMap map = map5();
+        assertEquals("A", (String)map.get(one));
+        ConcurrentHashMap empty = new ConcurrentHashMap();
+        assertNull(map.get("anything"));
+        assertNull(empty.get("anything"));
+    }
+
+    /**
+     * isEmpty is true of empty map and false for non-empty
+     */
+    public void testIsEmpty() {
+        ConcurrentHashMap empty = new ConcurrentHashMap();
+        ConcurrentHashMap map = map5();
+        assertTrue(empty.isEmpty());
+        assertFalse(map.isEmpty());
+    }
+
+    /**
+     * keys returns an enumeration containing all the keys from the map
+     */
+    public void testKeys() {
+        ConcurrentHashMap map = map5();
+        Enumeration e = map.keys();
+        int count = 0;
+        while (e.hasMoreElements()) {
+            count++;
+            e.nextElement();
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * keySet returns a Set containing all the keys
+     */
+    public void testKeySet() {
+        ConcurrentHashMap map = map5();
+        Set s = map.keySet();
+        assertEquals(5, s.size());
+        assertTrue(s.contains(one));
+        assertTrue(s.contains(two));
+        assertTrue(s.contains(three));
+        assertTrue(s.contains(four));
+        assertTrue(s.contains(five));
+    }
+
+    /**
+     * keySet.toArray returns contains all keys
+     */
+    public void testKeySetToArray() {
+        ConcurrentHashMap map = map5();
+        Set s = map.keySet();
+        Object[] ar = s.toArray();
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        assertEquals(5, ar.length);
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * Values.toArray contains all values
+     */
+    public void testValuesToArray() {
+        ConcurrentHashMap map = map5();
+        Collection v = map.values();
+        Object[] ar = v.toArray();
+        ArrayList s = new ArrayList(Arrays.asList(ar));
+        assertEquals(5, ar.length);
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * entrySet.toArray contains all entries
+     */
+    public void testEntrySetToArray() {
+        ConcurrentHashMap map = map5();
+        Set s = map.entrySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        for (int i = 0; i < 5; ++i) {
+            assertTrue(map.containsKey(((Map.Entry)(ar[i])).getKey()));
+            assertTrue(map.containsValue(((Map.Entry)(ar[i])).getValue()));
+        }
+    }
+
+    /**
+     * values collection contains all values
+     */
+    public void testValues() {
+        ConcurrentHashMap map = map5();
+        Collection s = map.values();
+        assertEquals(5, s.size());
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * entrySet contains all pairs
+     */
+    public void testEntrySet() {
+        ConcurrentHashMap map = map5();
+        Set s = map.entrySet();
+        assertEquals(5, s.size());
+        Iterator it = s.iterator();
+        while (it.hasNext()) {
+            Map.Entry e = (Map.Entry) it.next();
+            assertTrue(
+                       (e.getKey().equals(one) && e.getValue().equals("A")) ||
+                       (e.getKey().equals(two) && e.getValue().equals("B")) ||
+                       (e.getKey().equals(three) && e.getValue().equals("C")) ||
+                       (e.getKey().equals(four) && e.getValue().equals("D")) ||
+                       (e.getKey().equals(five) && e.getValue().equals("E")));
+        }
+    }
+
+    /**
+     * putAll adds all key-value pairs from the given map
+     */
+    public void testPutAll() {
+        ConcurrentHashMap empty = new ConcurrentHashMap();
+        ConcurrentHashMap map = map5();
+        empty.putAll(map);
+        assertEquals(5, empty.size());
+        assertTrue(empty.containsKey(one));
+        assertTrue(empty.containsKey(two));
+        assertTrue(empty.containsKey(three));
+        assertTrue(empty.containsKey(four));
+        assertTrue(empty.containsKey(five));
+    }
+
+    /**
+     * putIfAbsent works when the given key is not present
+     */
+    public void testPutIfAbsent() {
+        ConcurrentHashMap map = map5();
+        map.putIfAbsent(six, "Z");
+        assertTrue(map.containsKey(six));
+    }
+
+    /**
+     * putIfAbsent does not add the pair if the key is already present
+     */
+    public void testPutIfAbsent2() {
+        ConcurrentHashMap map = map5();
+        assertEquals("A", map.putIfAbsent(one, "Z"));
+    }
+
+    /**
+     * replace fails when the given key is not present
+     */
+    public void testReplace() {
+        ConcurrentHashMap map = map5();
+        assertNull(map.replace(six, "Z"));
+        assertFalse(map.containsKey(six));
+    }
+
+    /**
+     * replace succeeds if the key is already present
+     */
+    public void testReplace2() {
+        ConcurrentHashMap map = map5();
+        assertNotNull(map.replace(one, "Z"));
+        assertEquals("Z", map.get(one));
+    }
+
+    /**
+     * replace value fails when the given key not mapped to expected value
+     */
+    public void testReplaceValue() {
+        ConcurrentHashMap map = map5();
+        assertEquals("A", map.get(one));
+        assertFalse(map.replace(one, "Z", "Z"));
+        assertEquals("A", map.get(one));
+    }
+
+    /**
+     * replace value succeeds when the given key mapped to expected value
+     */
+    public void testReplaceValue2() {
+        ConcurrentHashMap map = map5();
+        assertEquals("A", map.get(one));
+        assertTrue(map.replace(one, "A", "Z"));
+        assertEquals("Z", map.get(one));
+    }
+
+    /**
+     * remove removes the correct key-value pair from the map
+     */
+    public void testRemove() {
+        ConcurrentHashMap map = map5();
+        map.remove(five);
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(five));
+    }
+
+    /**
+     * remove(key,value) removes only if pair present
+     */
+    public void testRemove2() {
+        ConcurrentHashMap map = map5();
+        map.remove(five, "E");
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(five));
+        map.remove(four, "A");
+        assertEquals(4, map.size());
+        assertTrue(map.containsKey(four));
+    }
+
+    /**
+     * size returns the correct values
+     */
+    public void testSize() {
+        ConcurrentHashMap map = map5();
+        ConcurrentHashMap empty = new ConcurrentHashMap();
+        assertEquals(0, empty.size());
+        assertEquals(5, map.size());
+    }
+
+    /**
+     * toString contains toString of elements
+     */
+    public void testToString() {
+        ConcurrentHashMap map = map5();
+        String s = map.toString();
+        for (int i = 1; i <= 5; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    // Exception tests
+
+    /**
+     * Cannot create with only negative capacity
+     */
+    public void testConstructor1() {
+        try {
+            new ConcurrentHashMap(-1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor (initialCapacity, loadFactor) throws
+     * IllegalArgumentException if either argument is negative
+     */
+    public void testConstructor2() {
+        try {
+            new ConcurrentHashMap(-1, .75f);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+
+        try {
+            new ConcurrentHashMap(16, -1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor (initialCapacity, loadFactor, concurrencyLevel)
+     * throws IllegalArgumentException if any argument is negative
+     */
+    public void testConstructor3() {
+        try {
+            new ConcurrentHashMap(-1, .75f, 1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+
+        try {
+            new ConcurrentHashMap(16, -1, 1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+
+        try {
+            new ConcurrentHashMap(16, .75f, -1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * ConcurrentHashMap(map) throws NullPointerException if the given
+     * map is null
+     */
+    public void testConstructor4() {
+        try {
+            new ConcurrentHashMap(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * ConcurrentHashMap(map) creates a new map with the same mappings
+     * as the given map
+     */
+    public void testConstructor5() {
+        ConcurrentHashMap map1 = map5();
+        ConcurrentHashMap map2 = new ConcurrentHashMap(map5());
+        assertTrue(map2.equals(map1));
+        map2.put(one, "F");
+        assertFalse(map2.equals(map1));
+    }
+
+    /**
+     * get(null) throws NPE
+     */
+    public void testGet_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        try {
+            c.get(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsKey(null) throws NPE
+     */
+    public void testContainsKey_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        try {
+            c.containsKey(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsValue(null) throws NPE
+     */
+    public void testContainsValue_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        try {
+            c.containsValue(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * contains(null) throws NPE
+     */
+    public void testContains_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        try {
+            c.contains(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * put(null,x) throws NPE
+     */
+    public void testPut1_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        try {
+            c.put(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * put(x, null) throws NPE
+     */
+    public void testPut2_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        try {
+            c.put("whatever", null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * putIfAbsent(null, x) throws NPE
+     */
+    public void testPutIfAbsent1_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        try {
+            c.putIfAbsent(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(null, x) throws NPE
+     */
+    public void testReplace_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        try {
+            c.replace(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(null, x, y) throws NPE
+     */
+    public void testReplaceValue_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        try {
+            c.replace(null, one, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * putIfAbsent(x, null) throws NPE
+     */
+    public void testPutIfAbsent2_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        try {
+            c.putIfAbsent("whatever", null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(x, null) throws NPE
+     */
+    public void testReplace2_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        try {
+            c.replace("whatever", null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(x, null, y) throws NPE
+     */
+    public void testReplaceValue2_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        try {
+            c.replace("whatever", null, "A");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(x, y, null) throws NPE
+     */
+    public void testReplaceValue3_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        try {
+            c.replace("whatever", one, null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null) throws NPE
+     */
+    public void testRemove1_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        c.put("sadsdf", "asdads");
+        try {
+            c.remove(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null, x) throws NPE
+     */
+    public void testRemove2_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        c.put("sadsdf", "asdads");
+        try {
+            c.remove(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(x, null) returns false
+     */
+    public void testRemove3() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        c.put("sadsdf", "asdads");
+        assertFalse(c.remove("sadsdf", null));
+    }
+
+    /**
+     * A deserialized map equals original
+     */
+    public void testSerialization() throws Exception {
+        Map x = map5();
+        Map y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    /**
+     * SetValue of an EntrySet entry sets value in the map.
+     */
+    public void testSetValueWriteThrough() {
+        // Adapted from a bug report by Eric Zoerner
+        ConcurrentHashMap map = new ConcurrentHashMap(2, 5.0f, 1);
+        assertTrue(map.isEmpty());
+        for (int i = 0; i < 20; i++)
+            map.put(new Integer(i), new Integer(i));
+        assertFalse(map.isEmpty());
+        Map.Entry entry1 = (Map.Entry)map.entrySet().iterator().next();
+        // Unless it happens to be first (in which case remainder of
+        // test is skipped), remove a possibly-colliding key from map
+        // which, under some implementations, may cause entry1 to be
+        // cloned in map
+        if (!entry1.getKey().equals(new Integer(16))) {
+            map.remove(new Integer(16));
+            entry1.setValue("XYZ");
+            assertTrue(map.containsValue("XYZ")); // fails if write-through broken
+        }
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/ConcurrentLinkedDequeTest.java b/jdk/test/java/util/concurrent/tck/ConcurrentLinkedDequeTest.java
new file mode 100644
index 00000000000..daab5381529
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/ConcurrentLinkedDequeTest.java
@@ -0,0 +1,926 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.Random;
+import java.util.concurrent.ConcurrentLinkedDeque;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ConcurrentLinkedDequeTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(ConcurrentLinkedDequeTest.class);
+    }
+
+    /**
+     * Returns a new deque of given size containing consecutive
+     * Integers 0 ... n.
+     */
+    private ConcurrentLinkedDeque<Integer> populatedDeque(int n) {
+        ConcurrentLinkedDeque<Integer> q = new ConcurrentLinkedDeque<Integer>();
+        assertTrue(q.isEmpty());
+        for (int i = 0; i < n; ++i)
+            assertTrue(q.offer(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(n, q.size());
+        return q;
+    }
+
+    /**
+     * new deque is empty
+     */
+    public void testConstructor1() {
+        assertTrue(new ConcurrentLinkedDeque().isEmpty());
+        assertEquals(0, new ConcurrentLinkedDeque().size());
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            new ConcurrentLinkedDeque((Collection)null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NPE
+     */
+    public void testConstructor4() {
+        try {
+            new ConcurrentLinkedDeque(Arrays.asList(new Integer[SIZE]));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws NPE
+     */
+    public void testConstructor5() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            new ConcurrentLinkedDeque(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Deque contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        assertTrue(q.isEmpty());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        q.add(two);
+        q.remove();
+        q.remove();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size() changes when elements added and removed
+     */
+    public void testSize() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i, q.size());
+            q.remove();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * push(null) throws NPE
+     */
+    public void testPushNull() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        try {
+            q.push(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * peekFirst() returns element inserted with push
+     */
+    public void testPush() {
+        ConcurrentLinkedDeque q = populatedDeque(3);
+        q.pollLast();
+        q.push(four);
+        assertSame(four, q.peekFirst());
+    }
+
+    /**
+     * pop() removes first element, or throws NSEE if empty
+     */
+    public void testPop() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pop());
+        }
+        try {
+            q.pop();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * offer(null) throws NPE
+     */
+    public void testOfferNull() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        try {
+            q.offer(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * offerFirst(null) throws NPE
+     */
+    public void testOfferFirstNull() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        try {
+            q.offerFirst(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * offerLast(null) throws NPE
+     */
+    public void testOfferLastNull() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        try {
+            q.offerLast(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * offer(x) succeeds
+     */
+    public void testOffer() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        assertTrue(q.offer(zero));
+        assertTrue(q.offer(one));
+        assertSame(zero, q.peekFirst());
+        assertSame(one, q.peekLast());
+    }
+
+    /**
+     * offerFirst(x) succeeds
+     */
+    public void testOfferFirst() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        assertTrue(q.offerFirst(zero));
+        assertTrue(q.offerFirst(one));
+        assertSame(one, q.peekFirst());
+        assertSame(zero, q.peekLast());
+    }
+
+    /**
+     * offerLast(x) succeeds
+     */
+    public void testOfferLast() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        assertTrue(q.offerLast(zero));
+        assertTrue(q.offerLast(one));
+        assertSame(zero, q.peekFirst());
+        assertSame(one, q.peekLast());
+    }
+
+    /**
+     * add(null) throws NPE
+     */
+    public void testAddNull() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        try {
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addFirst(null) throws NPE
+     */
+    public void testAddFirstNull() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        try {
+            q.addFirst(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addLast(null) throws NPE
+     */
+    public void testAddLastNull() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        try {
+            q.addLast(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * add(x) succeeds
+     */
+    public void testAdd() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        assertTrue(q.add(zero));
+        assertTrue(q.add(one));
+        assertSame(zero, q.peekFirst());
+        assertSame(one, q.peekLast());
+    }
+
+    /**
+     * addFirst(x) succeeds
+     */
+    public void testAddFirst() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        q.addFirst(zero);
+        q.addFirst(one);
+        assertSame(one, q.peekFirst());
+        assertSame(zero, q.peekLast());
+    }
+
+    /**
+     * addLast(x) succeeds
+     */
+    public void testAddLast() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        q.addLast(zero);
+        q.addLast(one);
+        assertSame(zero, q.peekFirst());
+        assertSame(one, q.peekLast());
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        try {
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll(this) throws IAE
+     */
+    public void testAddAllSelf() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        try {
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testAddAll2() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        try {
+            q.addAll(Arrays.asList(new Integer[SIZE]));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Deque contains all elements, in traversal order, of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * pollFirst() succeeds unless empty
+     */
+    public void testPollFirst() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * pollLast() succeeds unless empty
+     */
+    public void testPollLast() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.pollLast());
+        }
+        assertNull(q.pollLast());
+    }
+
+    /**
+     * poll() succeeds unless empty
+     */
+    public void testPoll() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll());
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * peek() returns next element, or null if empty
+     */
+    public void testPeek() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peek());
+            assertEquals(i, q.poll());
+            assertTrue(q.peek() == null ||
+                       !q.peek().equals(i));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * element() returns first element, or throws NSEE if empty
+     */
+    public void testElement() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.element());
+            assertEquals(i, q.poll());
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove() removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testRemoveElement() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertTrue(q.contains(i - 1));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertFalse(q.remove(i + 1));
+            assertFalse(q.contains(i + 1));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * peekFirst() returns next element, or null if empty
+     */
+    public void testPeekFirst() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peekFirst());
+            assertEquals(i, q.pollFirst());
+            assertTrue(q.peekFirst() == null ||
+                       !q.peekFirst().equals(i));
+        }
+        assertNull(q.peekFirst());
+    }
+
+    /**
+     * peekLast() returns next element, or null if empty
+     */
+    public void testPeekLast() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.peekLast());
+            assertEquals(i, q.pollLast());
+            assertTrue(q.peekLast() == null ||
+                       !q.peekLast().equals(i));
+        }
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * getFirst() returns first element, or throws NSEE if empty
+     */
+    public void testFirstElement() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.getFirst());
+            assertEquals(i, q.pollFirst());
+        }
+        try {
+            q.getFirst();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * getLast() returns last element, or throws NSEE if empty
+     */
+    public void testLastElement() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.getLast());
+            assertEquals(i, q.pollLast());
+        }
+        try {
+            q.getLast();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * removeFirst() removes first element, or throws NSEE if empty
+     */
+    public void testRemoveFirst() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.removeFirst());
+        }
+        try {
+            q.removeFirst();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekFirst());
+    }
+
+    /**
+     * removeLast() removes last element, or throws NSEE if empty
+     */
+    public void testRemoveLast() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.removeLast());
+        }
+        try {
+            q.removeLast();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * removeFirstOccurrence(x) removes x and returns true if present
+     */
+    public void testRemoveFirstOccurrence() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.removeFirstOccurrence(new Integer(i)));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.removeFirstOccurrence(new Integer(i)));
+            assertFalse(q.removeFirstOccurrence(new Integer(i + 1)));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * removeLastOccurrence(x) removes x and returns true if present
+     */
+    public void testRemoveLastOccurrence() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.removeLastOccurrence(new Integer(i)));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.removeLastOccurrence(new Integer(i)));
+            assertFalse(q.removeLastOccurrence(new Integer(i + 1)));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.poll();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear() removes all elements
+     */
+    public void testClear() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        ConcurrentLinkedDeque p = new ConcurrentLinkedDeque();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if change
+     */
+    public void testRetainAll() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        ConcurrentLinkedDeque p = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            ConcurrentLinkedDeque q = populatedDeque(SIZE);
+            ConcurrentLinkedDeque p = populatedDeque(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer x = (Integer)(p.remove());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    /**
+     * toArray() contains all elements in FIFO order
+     */
+    public void testToArray() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.poll());
+    }
+
+    /**
+     * toArray(a) contains all elements in FIFO order
+     */
+    public void testToArray2() {
+        ConcurrentLinkedDeque<Integer> q = populatedDeque(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.poll());
+    }
+
+    /**
+     * toArray(null) throws NullPointerException
+     */
+    public void testToArray_NullArg() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        try {
+            q.toArray(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray1_BadArg() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        try {
+            q.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * Iterator iterates through all elements
+     */
+    public void testIterator() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        Iterator it = q.iterator();
+        int i;
+        for (i = 0; it.hasNext(); i++)
+            assertTrue(q.contains(it.next()));
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty collection has no elements
+     */
+    public void testEmptyIterator() {
+        Deque c = new ConcurrentLinkedDeque();
+        assertIteratorExhausted(c.iterator());
+        assertIteratorExhausted(c.descendingIterator());
+    }
+
+    /**
+     * Iterator ordering is FIFO
+     */
+    public void testIteratorOrdering() {
+        final ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        q.add(one);
+        q.add(two);
+        q.add(three);
+
+        int k = 0;
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            assertEquals(++k, it.next());
+        }
+
+        assertEquals(3, k);
+    }
+
+    /**
+     * Modifications do not cause iterators to fail
+     */
+    public void testWeaklyConsistentIteration() {
+        final ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        q.add(one);
+        q.add(two);
+        q.add(three);
+
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            q.remove();
+            it.next();
+        }
+
+        assertEquals("deque should be empty again", 0, q.size());
+    }
+
+    /**
+     * iterator.remove() removes current element
+     */
+    public void testIteratorRemove() {
+        final ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        final Random rng = new Random();
+        for (int iters = 0; iters < 100; ++iters) {
+            int max = rng.nextInt(5) + 2;
+            int split = rng.nextInt(max - 1) + 1;
+            for (int j = 1; j <= max; ++j)
+                q.add(new Integer(j));
+            Iterator it = q.iterator();
+            for (int j = 1; j <= split; ++j)
+                assertEquals(it.next(), new Integer(j));
+            it.remove();
+            assertEquals(it.next(), new Integer(split + 1));
+            for (int j = 1; j <= split; ++j)
+                q.remove(new Integer(j));
+            it = q.iterator();
+            for (int j = split + 1; j <= max; ++j) {
+                assertEquals(it.next(), new Integer(j));
+                it.remove();
+            }
+            assertFalse(it.hasNext());
+            assertTrue(q.isEmpty());
+        }
+    }
+
+    /**
+     * Descending iterator iterates through all elements
+     */
+    public void testDescendingIterator() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        int i = 0;
+        Iterator it = q.descendingIterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+        assertFalse(it.hasNext());
+        try {
+            it.next();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * Descending iterator ordering is reverse FIFO
+     */
+    public void testDescendingIteratorOrdering() {
+        final ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        for (int iters = 0; iters < 100; ++iters) {
+            q.add(new Integer(3));
+            q.add(new Integer(2));
+            q.add(new Integer(1));
+            int k = 0;
+            for (Iterator it = q.descendingIterator(); it.hasNext();) {
+                assertEquals(++k, it.next());
+            }
+
+            assertEquals(3, k);
+            q.remove();
+            q.remove();
+            q.remove();
+        }
+    }
+
+    /**
+     * descendingIterator.remove() removes current element
+     */
+    public void testDescendingIteratorRemove() {
+        final ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        final Random rng = new Random();
+        for (int iters = 0; iters < 100; ++iters) {
+            int max = rng.nextInt(5) + 2;
+            int split = rng.nextInt(max - 1) + 1;
+            for (int j = max; j >= 1; --j)
+                q.add(new Integer(j));
+            Iterator it = q.descendingIterator();
+            for (int j = 1; j <= split; ++j)
+                assertEquals(it.next(), new Integer(j));
+            it.remove();
+            assertEquals(it.next(), new Integer(split + 1));
+            for (int j = 1; j <= split; ++j)
+                q.remove(new Integer(j));
+            it = q.descendingIterator();
+            for (int j = split + 1; j <= max; ++j) {
+                assertEquals(it.next(), new Integer(j));
+                it.remove();
+            }
+            assertFalse(it.hasNext());
+            assertTrue(q.isEmpty());
+        }
+    }
+
+    /**
+     * toString() contains toStrings of elements
+     */
+    public void testToString() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized deque has same elements in same order
+     */
+    public void testSerialization() throws Exception {
+        Queue x = populatedDeque(SIZE);
+        Queue y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertTrue(Arrays.equals(x.toArray(), y.toArray()));
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(), y.remove());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * contains(null) always return false.
+     * remove(null) always throws NullPointerException.
+     */
+    public void testNeverContainsNull() {
+        Deque<?>[] qs = {
+            new ConcurrentLinkedDeque<Object>(),
+            populatedDeque(2),
+        };
+
+        for (Deque<?> q : qs) {
+            assertFalse(q.contains(null));
+            try {
+                assertFalse(q.remove(null));
+                shouldThrow();
+            } catch (NullPointerException success) {}
+            try {
+                assertFalse(q.removeFirstOccurrence(null));
+                shouldThrow();
+            } catch (NullPointerException success) {}
+            try {
+                assertFalse(q.removeLastOccurrence(null));
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+}
diff --git a/jdk/test/java/util/concurrent/tck/ConcurrentLinkedQueueTest.java b/jdk/test/java/util/concurrent/tck/ConcurrentLinkedQueueTest.java
new file mode 100644
index 00000000000..bafaa6581f0
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/ConcurrentLinkedQueueTest.java
@@ -0,0 +1,564 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ConcurrentLinkedQueueTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(ConcurrentLinkedQueueTest.class);
+    }
+
+    /**
+     * Returns a new queue of given size containing consecutive
+     * Integers 0 ... n.
+     */
+    private ConcurrentLinkedQueue<Integer> populatedQueue(int n) {
+        ConcurrentLinkedQueue<Integer> q = new ConcurrentLinkedQueue<Integer>();
+        assertTrue(q.isEmpty());
+        for (int i = 0; i < n; ++i)
+            assertTrue(q.offer(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(n, q.size());
+        return q;
+    }
+
+    /**
+     * new queue is empty
+     */
+    public void testConstructor1() {
+        assertEquals(0, new ConcurrentLinkedQueue().size());
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            new ConcurrentLinkedQueue((Collection)null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NPE
+     */
+    public void testConstructor4() {
+        try {
+            new ConcurrentLinkedQueue(Arrays.asList(new Integer[SIZE]));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws NPE
+     */
+    public void testConstructor5() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            new ConcurrentLinkedQueue(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        ConcurrentLinkedQueue q = new ConcurrentLinkedQueue(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        assertTrue(q.isEmpty());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        q.add(two);
+        q.remove();
+        q.remove();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testSize() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i, q.size());
+            q.remove();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * offer(null) throws NPE
+     */
+    public void testOfferNull() {
+        ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        try {
+            q.offer(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * add(null) throws NPE
+     */
+    public void testAddNull() {
+        ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        try {
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Offer returns true
+     */
+    public void testOffer() {
+        ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        assertTrue(q.offer(zero));
+        assertTrue(q.offer(one));
+    }
+
+    /**
+     * add returns true
+     */
+    public void testAdd() {
+        ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            assertTrue(q.add(new Integer(i)));
+        }
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        try {
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll(this) throws IAE
+     */
+    public void testAddAllSelf() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        try {
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testAddAll2() {
+        ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        try {
+            q.addAll(Arrays.asList(new Integer[SIZE]));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements, in traversal order, of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll());
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * peek returns next element, or null if empty
+     */
+    public void testPeek() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peek());
+            assertEquals(i, q.poll());
+            assertTrue(q.peek() == null ||
+                       !q.peek().equals(i));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * element returns next element, or throws NSEE if empty
+     */
+    public void testElement() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.element());
+            assertEquals(i, q.poll());
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testRemoveElement() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertTrue(q.contains(i - 1));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertFalse(q.remove(i + 1));
+            assertFalse(q.contains(i + 1));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.poll();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        ConcurrentLinkedQueue p = new ConcurrentLinkedQueue();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if change
+     */
+    public void testRetainAll() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        ConcurrentLinkedQueue p = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            ConcurrentLinkedQueue q = populatedQueue(SIZE);
+            ConcurrentLinkedQueue p = populatedQueue(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer x = (Integer)(p.remove());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    /**
+     * toArray contains all elements in FIFO order
+     */
+    public void testToArray() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.poll());
+    }
+
+    /**
+     * toArray(a) contains all elements in FIFO order
+     */
+    public void testToArray2() {
+        ConcurrentLinkedQueue<Integer> q = populatedQueue(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.poll());
+    }
+
+    /**
+     * toArray(null) throws NullPointerException
+     */
+    public void testToArray_NullArg() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        try {
+            q.toArray(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray1_BadArg() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        try {
+            q.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        Iterator it = q.iterator();
+        int i;
+        for (i = 0; it.hasNext(); i++)
+            assertTrue(q.contains(it.next()));
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty collection has no elements
+     */
+    public void testEmptyIterator() {
+        assertIteratorExhausted(new ConcurrentLinkedQueue().iterator());
+    }
+
+    /**
+     * iterator ordering is FIFO
+     */
+    public void testIteratorOrdering() {
+        final ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        q.add(one);
+        q.add(two);
+        q.add(three);
+
+        int k = 0;
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            assertEquals(++k, it.next());
+        }
+
+        assertEquals(3, k);
+    }
+
+    /**
+     * Modifications do not cause iterators to fail
+     */
+    public void testWeaklyConsistentIteration() {
+        final ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        q.add(one);
+        q.add(two);
+        q.add(three);
+
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            q.remove();
+            it.next();
+        }
+
+        assertEquals("queue should be empty again", 0, q.size());
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+        it = q.iterator();
+        assertSame(it.next(), two);
+        assertSame(it.next(), three);
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized queue has same elements in same order
+     */
+    public void testSerialization() throws Exception {
+        Queue x = populatedQueue(SIZE);
+        Queue y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertTrue(Arrays.equals(x.toArray(), y.toArray()));
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(), y.remove());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * remove(null), contains(null) always return false
+     */
+    public void testNeverContainsNull() {
+        Collection<?>[] qs = {
+            new ConcurrentLinkedQueue<Object>(),
+            populatedQueue(2),
+        };
+
+        for (Collection<?> q : qs) {
+            assertFalse(q.contains(null));
+            assertFalse(q.remove(null));
+        }
+    }
+}
diff --git a/jdk/test/java/util/concurrent/tck/ConcurrentSkipListMapTest.java b/jdk/test/java/util/concurrent/tck/ConcurrentSkipListMapTest.java
new file mode 100644
index 00000000000..515303e3e64
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/ConcurrentSkipListMapTest.java
@@ -0,0 +1,1306 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.NavigableSet;
+import java.util.NoSuchElementException;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.ConcurrentSkipListMap;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ConcurrentSkipListMapTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ConcurrentSkipListMapTest.class);
+    }
+
+    /**
+     * Returns a new map from Integers 1-5 to Strings "A"-"E".
+     */
+    private static ConcurrentSkipListMap map5() {
+        ConcurrentSkipListMap map = new ConcurrentSkipListMap();
+        assertTrue(map.isEmpty());
+        map.put(one, "A");
+        map.put(five, "E");
+        map.put(three, "C");
+        map.put(two, "B");
+        map.put(four, "D");
+        assertFalse(map.isEmpty());
+        assertEquals(5, map.size());
+        return map;
+    }
+
+    /**
+     * clear removes all pairs
+     */
+    public void testClear() {
+        ConcurrentSkipListMap map = map5();
+        map.clear();
+        assertEquals(0, map.size());
+    }
+
+    /**
+     * copy constructor creates map equal to source map
+     */
+    public void testConstructFromSorted() {
+        ConcurrentSkipListMap map = map5();
+        ConcurrentSkipListMap map2 = new ConcurrentSkipListMap(map);
+        assertEquals(map, map2);
+    }
+
+    /**
+     * Maps with same contents are equal
+     */
+    public void testEquals() {
+        ConcurrentSkipListMap map1 = map5();
+        ConcurrentSkipListMap map2 = map5();
+        assertEquals(map1, map2);
+        assertEquals(map2, map1);
+        map1.clear();
+        assertFalse(map1.equals(map2));
+        assertFalse(map2.equals(map1));
+    }
+
+    /**
+     * containsKey returns true for contained key
+     */
+    public void testContainsKey() {
+        ConcurrentSkipListMap map = map5();
+        assertTrue(map.containsKey(one));
+        assertFalse(map.containsKey(zero));
+    }
+
+    /**
+     * containsValue returns true for held values
+     */
+    public void testContainsValue() {
+        ConcurrentSkipListMap map = map5();
+        assertTrue(map.containsValue("A"));
+        assertFalse(map.containsValue("Z"));
+    }
+
+    /**
+     * get returns the correct element at the given key,
+     * or null if not present
+     */
+    public void testGet() {
+        ConcurrentSkipListMap map = map5();
+        assertEquals("A", (String)map.get(one));
+        ConcurrentSkipListMap empty = new ConcurrentSkipListMap();
+        assertNull(empty.get(one));
+    }
+
+    /**
+     * isEmpty is true of empty map and false for non-empty
+     */
+    public void testIsEmpty() {
+        ConcurrentSkipListMap empty = new ConcurrentSkipListMap();
+        ConcurrentSkipListMap map = map5();
+        assertTrue(empty.isEmpty());
+        assertFalse(map.isEmpty());
+    }
+
+    /**
+     * firstKey returns first key
+     */
+    public void testFirstKey() {
+        ConcurrentSkipListMap map = map5();
+        assertEquals(one, map.firstKey());
+    }
+
+    /**
+     * lastKey returns last key
+     */
+    public void testLastKey() {
+        ConcurrentSkipListMap map = map5();
+        assertEquals(five, map.lastKey());
+    }
+
+    /**
+     * keySet.toArray returns contains all keys
+     */
+    public void testKeySetToArray() {
+        ConcurrentSkipListMap map = map5();
+        Set s = map.keySet();
+        Object[] ar = s.toArray();
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        assertEquals(5, ar.length);
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * descendingkeySet.toArray returns contains all keys
+     */
+    public void testDescendingKeySetToArray() {
+        ConcurrentSkipListMap map = map5();
+        Set s = map.descendingKeySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * keySet returns a Set containing all the keys
+     */
+    public void testKeySet() {
+        ConcurrentSkipListMap map = map5();
+        Set s = map.keySet();
+        assertEquals(5, s.size());
+        assertTrue(s.contains(one));
+        assertTrue(s.contains(two));
+        assertTrue(s.contains(three));
+        assertTrue(s.contains(four));
+        assertTrue(s.contains(five));
+    }
+
+    /**
+     * keySet is ordered
+     */
+    public void testKeySetOrder() {
+        ConcurrentSkipListMap map = map5();
+        Set s = map.keySet();
+        Iterator i = s.iterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, one);
+        int count = 1;
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) < 0);
+            last = k;
+            ++count;
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * descending iterator of key set is inverse ordered
+     */
+    public void testKeySetDescendingIteratorOrder() {
+        ConcurrentSkipListMap map = map5();
+        NavigableSet s = map.navigableKeySet();
+        Iterator i = s.descendingIterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, five);
+        int count = 1;
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) > 0);
+            last = k;
+            ++count;
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * descendingKeySet is ordered
+     */
+    public void testDescendingKeySetOrder() {
+        ConcurrentSkipListMap map = map5();
+        Set s = map.descendingKeySet();
+        Iterator i = s.iterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, five);
+        int count = 1;
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) > 0);
+            last = k;
+            ++count;
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * descending iterator of descendingKeySet is ordered
+     */
+    public void testDescendingKeySetDescendingIteratorOrder() {
+        ConcurrentSkipListMap map = map5();
+        NavigableSet s = map.descendingKeySet();
+        Iterator i = s.descendingIterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, one);
+        int count = 1;
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) < 0);
+            last = k;
+            ++count;
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * Values.toArray contains all values
+     */
+    public void testValuesToArray() {
+        ConcurrentSkipListMap map = map5();
+        Collection v = map.values();
+        Object[] ar = v.toArray();
+        ArrayList s = new ArrayList(Arrays.asList(ar));
+        assertEquals(5, ar.length);
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * values collection contains all values
+     */
+    public void testValues() {
+        ConcurrentSkipListMap map = map5();
+        Collection s = map.values();
+        assertEquals(5, s.size());
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * entrySet contains all pairs
+     */
+    public void testEntrySet() {
+        ConcurrentSkipListMap map = map5();
+        Set s = map.entrySet();
+        assertEquals(5, s.size());
+        Iterator it = s.iterator();
+        while (it.hasNext()) {
+            Map.Entry e = (Map.Entry) it.next();
+            assertTrue(
+                       (e.getKey().equals(one) && e.getValue().equals("A")) ||
+                       (e.getKey().equals(two) && e.getValue().equals("B")) ||
+                       (e.getKey().equals(three) && e.getValue().equals("C")) ||
+                       (e.getKey().equals(four) && e.getValue().equals("D")) ||
+                       (e.getKey().equals(five) && e.getValue().equals("E")));
+        }
+    }
+
+    /**
+     * descendingEntrySet contains all pairs
+     */
+    public void testDescendingEntrySet() {
+        ConcurrentSkipListMap map = map5();
+        Set s = map.descendingMap().entrySet();
+        assertEquals(5, s.size());
+        Iterator it = s.iterator();
+        while (it.hasNext()) {
+            Map.Entry e = (Map.Entry) it.next();
+            assertTrue(
+                       (e.getKey().equals(one) && e.getValue().equals("A")) ||
+                       (e.getKey().equals(two) && e.getValue().equals("B")) ||
+                       (e.getKey().equals(three) && e.getValue().equals("C")) ||
+                       (e.getKey().equals(four) && e.getValue().equals("D")) ||
+                       (e.getKey().equals(five) && e.getValue().equals("E")));
+        }
+    }
+
+    /**
+     * entrySet.toArray contains all entries
+     */
+    public void testEntrySetToArray() {
+        ConcurrentSkipListMap map = map5();
+        Set s = map.entrySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        for (int i = 0; i < 5; ++i) {
+            assertTrue(map.containsKey(((Map.Entry)(ar[i])).getKey()));
+            assertTrue(map.containsValue(((Map.Entry)(ar[i])).getValue()));
+        }
+    }
+
+    /**
+     * descendingEntrySet.toArray contains all entries
+     */
+    public void testDescendingEntrySetToArray() {
+        ConcurrentSkipListMap map = map5();
+        Set s = map.descendingMap().entrySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        for (int i = 0; i < 5; ++i) {
+            assertTrue(map.containsKey(((Map.Entry)(ar[i])).getKey()));
+            assertTrue(map.containsValue(((Map.Entry)(ar[i])).getValue()));
+        }
+    }
+
+    /**
+     * putAll adds all key-value pairs from the given map
+     */
+    public void testPutAll() {
+        ConcurrentSkipListMap empty = new ConcurrentSkipListMap();
+        ConcurrentSkipListMap map = map5();
+        empty.putAll(map);
+        assertEquals(5, empty.size());
+        assertTrue(empty.containsKey(one));
+        assertTrue(empty.containsKey(two));
+        assertTrue(empty.containsKey(three));
+        assertTrue(empty.containsKey(four));
+        assertTrue(empty.containsKey(five));
+    }
+
+    /**
+     * putIfAbsent works when the given key is not present
+     */
+    public void testPutIfAbsent() {
+        ConcurrentSkipListMap map = map5();
+        map.putIfAbsent(six, "Z");
+        assertTrue(map.containsKey(six));
+    }
+
+    /**
+     * putIfAbsent does not add the pair if the key is already present
+     */
+    public void testPutIfAbsent2() {
+        ConcurrentSkipListMap map = map5();
+        assertEquals("A", map.putIfAbsent(one, "Z"));
+    }
+
+    /**
+     * replace fails when the given key is not present
+     */
+    public void testReplace() {
+        ConcurrentSkipListMap map = map5();
+        assertNull(map.replace(six, "Z"));
+        assertFalse(map.containsKey(six));
+    }
+
+    /**
+     * replace succeeds if the key is already present
+     */
+    public void testReplace2() {
+        ConcurrentSkipListMap map = map5();
+        assertNotNull(map.replace(one, "Z"));
+        assertEquals("Z", map.get(one));
+    }
+
+    /**
+     * replace value fails when the given key not mapped to expected value
+     */
+    public void testReplaceValue() {
+        ConcurrentSkipListMap map = map5();
+        assertEquals("A", map.get(one));
+        assertFalse(map.replace(one, "Z", "Z"));
+        assertEquals("A", map.get(one));
+    }
+
+    /**
+     * replace value succeeds when the given key mapped to expected value
+     */
+    public void testReplaceValue2() {
+        ConcurrentSkipListMap map = map5();
+        assertEquals("A", map.get(one));
+        assertTrue(map.replace(one, "A", "Z"));
+        assertEquals("Z", map.get(one));
+    }
+
+    /**
+     * remove removes the correct key-value pair from the map
+     */
+    public void testRemove() {
+        ConcurrentSkipListMap map = map5();
+        map.remove(five);
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(five));
+    }
+
+    /**
+     * remove(key,value) removes only if pair present
+     */
+    public void testRemove2() {
+        ConcurrentSkipListMap map = map5();
+        assertTrue(map.containsKey(five));
+        assertEquals("E", map.get(five));
+        map.remove(five, "E");
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(five));
+        map.remove(four, "A");
+        assertEquals(4, map.size());
+        assertTrue(map.containsKey(four));
+    }
+
+    /**
+     * lowerEntry returns preceding entry.
+     */
+    public void testLowerEntry() {
+        ConcurrentSkipListMap map = map5();
+        Map.Entry e1 = map.lowerEntry(three);
+        assertEquals(two, e1.getKey());
+
+        Map.Entry e2 = map.lowerEntry(six);
+        assertEquals(five, e2.getKey());
+
+        Map.Entry e3 = map.lowerEntry(one);
+        assertNull(e3);
+
+        Map.Entry e4 = map.lowerEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higherEntry returns next entry.
+     */
+    public void testHigherEntry() {
+        ConcurrentSkipListMap map = map5();
+        Map.Entry e1 = map.higherEntry(three);
+        assertEquals(four, e1.getKey());
+
+        Map.Entry e2 = map.higherEntry(zero);
+        assertEquals(one, e2.getKey());
+
+        Map.Entry e3 = map.higherEntry(five);
+        assertNull(e3);
+
+        Map.Entry e4 = map.higherEntry(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floorEntry returns preceding entry.
+     */
+    public void testFloorEntry() {
+        ConcurrentSkipListMap map = map5();
+        Map.Entry e1 = map.floorEntry(three);
+        assertEquals(three, e1.getKey());
+
+        Map.Entry e2 = map.floorEntry(six);
+        assertEquals(five, e2.getKey());
+
+        Map.Entry e3 = map.floorEntry(one);
+        assertEquals(one, e3.getKey());
+
+        Map.Entry e4 = map.floorEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceilingEntry returns next entry.
+     */
+    public void testCeilingEntry() {
+        ConcurrentSkipListMap map = map5();
+        Map.Entry e1 = map.ceilingEntry(three);
+        assertEquals(three, e1.getKey());
+
+        Map.Entry e2 = map.ceilingEntry(zero);
+        assertEquals(one, e2.getKey());
+
+        Map.Entry e3 = map.ceilingEntry(five);
+        assertEquals(five, e3.getKey());
+
+        Map.Entry e4 = map.ceilingEntry(six);
+        assertNull(e4);
+    }
+
+    /**
+     * lowerEntry, higherEntry, ceilingEntry, and floorEntry return
+     * immutable entries
+     */
+    public void testEntryImmutability() {
+        ConcurrentSkipListMap map = map5();
+        Map.Entry e = map.lowerEntry(three);
+        assertEquals(two, e.getKey());
+        try {
+            e.setValue("X");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.higherEntry(zero);
+        assertEquals(one, e.getKey());
+        try {
+            e.setValue("X");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.floorEntry(one);
+        assertEquals(one, e.getKey());
+        try {
+            e.setValue("X");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.ceilingEntry(five);
+        assertEquals(five, e.getKey());
+        try {
+            e.setValue("X");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+    }
+
+    /**
+     * lowerKey returns preceding element
+     */
+    public void testLowerKey() {
+        ConcurrentSkipListMap q = map5();
+        Object e1 = q.lowerKey(three);
+        assertEquals(two, e1);
+
+        Object e2 = q.lowerKey(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.lowerKey(one);
+        assertNull(e3);
+
+        Object e4 = q.lowerKey(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higherKey returns next element
+     */
+    public void testHigherKey() {
+        ConcurrentSkipListMap q = map5();
+        Object e1 = q.higherKey(three);
+        assertEquals(four, e1);
+
+        Object e2 = q.higherKey(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.higherKey(five);
+        assertNull(e3);
+
+        Object e4 = q.higherKey(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floorKey returns preceding element
+     */
+    public void testFloorKey() {
+        ConcurrentSkipListMap q = map5();
+        Object e1 = q.floorKey(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.floorKey(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.floorKey(one);
+        assertEquals(one, e3);
+
+        Object e4 = q.floorKey(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceilingKey returns next element
+     */
+    public void testCeilingKey() {
+        ConcurrentSkipListMap q = map5();
+        Object e1 = q.ceilingKey(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.ceilingKey(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.ceilingKey(five);
+        assertEquals(five, e3);
+
+        Object e4 = q.ceilingKey(six);
+        assertNull(e4);
+    }
+
+    /**
+     * pollFirstEntry returns entries in order
+     */
+    public void testPollFirstEntry() {
+        ConcurrentSkipListMap map = map5();
+        Map.Entry e = map.pollFirstEntry();
+        assertEquals(one, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(two, e.getKey());
+        map.put(one, "A");
+        e = map.pollFirstEntry();
+        assertEquals(one, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(three, e.getKey());
+        map.remove(four);
+        e = map.pollFirstEntry();
+        assertEquals(five, e.getKey());
+        try {
+            e.setValue("A");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollFirstEntry();
+        assertNull(e);
+    }
+
+    /**
+     * pollLastEntry returns entries in order
+     */
+    public void testPollLastEntry() {
+        ConcurrentSkipListMap map = map5();
+        Map.Entry e = map.pollLastEntry();
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(four, e.getKey());
+        map.put(five, "E");
+        e = map.pollLastEntry();
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(three, e.getKey());
+        map.remove(two);
+        e = map.pollLastEntry();
+        assertEquals(one, e.getKey());
+        try {
+            e.setValue("E");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollLastEntry();
+        assertNull(e);
+    }
+
+    /**
+     * size returns the correct values
+     */
+    public void testSize() {
+        ConcurrentSkipListMap map = map5();
+        ConcurrentSkipListMap empty = new ConcurrentSkipListMap();
+        assertEquals(0, empty.size());
+        assertEquals(5, map.size());
+    }
+
+    /**
+     * toString contains toString of elements
+     */
+    public void testToString() {
+        ConcurrentSkipListMap map = map5();
+        String s = map.toString();
+        for (int i = 1; i <= 5; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    // Exception tests
+
+    /**
+     * get(null) of nonempty map throws NPE
+     */
+    public void testGet_NullPointerException() {
+        ConcurrentSkipListMap c = map5();
+        try {
+            c.get(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsKey(null) of nonempty map throws NPE
+     */
+    public void testContainsKey_NullPointerException() {
+        ConcurrentSkipListMap c = map5();
+        try {
+            c.containsKey(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsValue(null) throws NPE
+     */
+    public void testContainsValue_NullPointerException() {
+        ConcurrentSkipListMap c = new ConcurrentSkipListMap();
+        try {
+            c.containsValue(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * put(null,x) throws NPE
+     */
+    public void testPut1_NullPointerException() {
+        ConcurrentSkipListMap c = map5();
+        try {
+            c.put(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * putIfAbsent(null, x) throws NPE
+     */
+    public void testPutIfAbsent1_NullPointerException() {
+        ConcurrentSkipListMap c = map5();
+        try {
+            c.putIfAbsent(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(null, x) throws NPE
+     */
+    public void testReplace_NullPointerException() {
+        ConcurrentSkipListMap c = map5();
+        try {
+            c.replace(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(null, x, y) throws NPE
+     */
+    public void testReplaceValue_NullPointerException() {
+        ConcurrentSkipListMap c = map5();
+        try {
+            c.replace(null, one, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null) throws NPE
+     */
+    public void testRemove1_NullPointerException() {
+        ConcurrentSkipListMap c = new ConcurrentSkipListMap();
+        c.put("sadsdf", "asdads");
+        try {
+            c.remove(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null, x) throws NPE
+     */
+    public void testRemove2_NullPointerException() {
+        ConcurrentSkipListMap c = new ConcurrentSkipListMap();
+        c.put("sadsdf", "asdads");
+        try {
+            c.remove(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(x, null) returns false
+     */
+    public void testRemove3() {
+        ConcurrentSkipListMap c = new ConcurrentSkipListMap();
+        c.put("sadsdf", "asdads");
+        assertFalse(c.remove("sadsdf", null));
+    }
+
+    /**
+     * A deserialized map equals original
+     */
+    public void testSerialization() throws Exception {
+        NavigableMap x = map5();
+        NavigableMap y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    /**
+     * subMap returns map with keys in requested range
+     */
+    public void testSubMapContents() {
+        ConcurrentSkipListMap map = map5();
+        NavigableMap sm = map.subMap(two, true, four, false);
+        assertEquals(two, sm.firstKey());
+        assertEquals(three, sm.lastKey());
+        assertEquals(2, sm.size());
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        Iterator r = sm.descendingKeySet().iterator();
+        k = (Integer)(r.next());
+        assertEquals(three, k);
+        k = (Integer)(r.next());
+        assertEquals(two, k);
+        assertFalse(r.hasNext());
+
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(two));
+        assertEquals(4, map.size());
+        assertEquals(1, sm.size());
+        assertEquals(three, sm.firstKey());
+        assertEquals(three, sm.lastKey());
+        assertEquals("C", sm.remove(three));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, map.size());
+    }
+
+    public void testSubMapContents2() {
+        ConcurrentSkipListMap map = map5();
+        NavigableMap sm = map.subMap(two, true, three, false);
+        assertEquals(1, sm.size());
+        assertEquals(two, sm.firstKey());
+        assertEquals(two, sm.lastKey());
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertFalse(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        assertFalse(i.hasNext());
+        Iterator r = sm.descendingKeySet().iterator();
+        k = (Integer)(r.next());
+        assertEquals(two, k);
+        assertFalse(r.hasNext());
+
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(two));
+        assertEquals(4, map.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertSame(sm.remove(three), null);
+        assertEquals(4, map.size());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testHeadMapContents() {
+        ConcurrentSkipListMap map = map5();
+        NavigableMap sm = map.headMap(four, false);
+        assertTrue(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(one, k);
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, map.size());
+        assertEquals(four, map.firstKey());
+    }
+
+    /**
+     * tailMap returns map with keys in requested range
+     */
+    public void testTailMapContents() {
+        ConcurrentSkipListMap map = map5();
+        NavigableMap sm = map.tailMap(two, true);
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertTrue(sm.containsKey(four));
+        assertTrue(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        k = (Integer)(i.next());
+        assertEquals(four, k);
+        k = (Integer)(i.next());
+        assertEquals(five, k);
+        assertFalse(i.hasNext());
+        Iterator r = sm.descendingKeySet().iterator();
+        k = (Integer)(r.next());
+        assertEquals(five, k);
+        k = (Integer)(r.next());
+        assertEquals(four, k);
+        k = (Integer)(r.next());
+        assertEquals(three, k);
+        k = (Integer)(r.next());
+        assertEquals(two, k);
+        assertFalse(r.hasNext());
+
+        Iterator ei = sm.entrySet().iterator();
+        Map.Entry e;
+        e = (Map.Entry)(ei.next());
+        assertEquals(two, e.getKey());
+        assertEquals("B", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(three, e.getKey());
+        assertEquals("C", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(four, e.getKey());
+        assertEquals("D", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        assertFalse(i.hasNext());
+
+        NavigableMap ssm = sm.tailMap(four, true);
+        assertEquals(four, ssm.firstKey());
+        assertEquals(five, ssm.lastKey());
+        assertEquals("D", ssm.remove(four));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, map.size());
+    }
+
+    Random rnd = new Random(666);
+    BitSet bs;
+
+    /**
+     * Submaps of submaps subdivide correctly
+     */
+    public void testRecursiveSubMaps() throws Exception {
+        int mapSize = expensiveTests ? 1000 : 100;
+        Class cl = ConcurrentSkipListMap.class;
+        NavigableMap<Integer, Integer> map = newMap(cl);
+        bs = new BitSet(mapSize);
+
+        populate(map, mapSize);
+        check(map,                 0, mapSize - 1, true);
+        check(map.descendingMap(), 0, mapSize - 1, false);
+
+        mutateMap(map, 0, mapSize - 1);
+        check(map,                 0, mapSize - 1, true);
+        check(map.descendingMap(), 0, mapSize - 1, false);
+
+        bashSubMap(map.subMap(0, true, mapSize, false),
+                   0, mapSize - 1, true);
+    }
+
+    static NavigableMap<Integer, Integer> newMap(Class cl) throws Exception {
+        NavigableMap<Integer, Integer> result =
+            (NavigableMap<Integer, Integer>) cl.newInstance();
+        assertEquals(0, result.size());
+        assertFalse(result.keySet().iterator().hasNext());
+        return result;
+    }
+
+    void populate(NavigableMap<Integer, Integer> map, int limit) {
+        for (int i = 0, n = 2 * limit / 3; i < n; i++) {
+            int key = rnd.nextInt(limit);
+            put(map, key);
+        }
+    }
+
+    void mutateMap(NavigableMap<Integer, Integer> map, int min, int max) {
+        int size = map.size();
+        int rangeSize = max - min + 1;
+
+        // Remove a bunch of entries directly
+        for (int i = 0, n = rangeSize / 2; i < n; i++) {
+            remove(map, min - 5 + rnd.nextInt(rangeSize + 10));
+        }
+
+        // Remove a bunch of entries with iterator
+        for (Iterator<Integer> it = map.keySet().iterator(); it.hasNext(); ) {
+            if (rnd.nextBoolean()) {
+                bs.clear(it.next());
+                it.remove();
+            }
+        }
+
+        // Add entries till we're back to original size
+        while (map.size() < size) {
+            int key = min + rnd.nextInt(rangeSize);
+            assertTrue(key >= min && key <= max);
+            put(map, key);
+        }
+    }
+
+    void mutateSubMap(NavigableMap<Integer, Integer> map, int min, int max) {
+        int size = map.size();
+        int rangeSize = max - min + 1;
+
+        // Remove a bunch of entries directly
+        for (int i = 0, n = rangeSize / 2; i < n; i++) {
+            remove(map, min - 5 + rnd.nextInt(rangeSize + 10));
+        }
+
+        // Remove a bunch of entries with iterator
+        for (Iterator<Integer> it = map.keySet().iterator(); it.hasNext(); ) {
+            if (rnd.nextBoolean()) {
+                bs.clear(it.next());
+                it.remove();
+            }
+        }
+
+        // Add entries till we're back to original size
+        while (map.size() < size) {
+            int key = min - 5 + rnd.nextInt(rangeSize + 10);
+            if (key >= min && key <= max) {
+                put(map, key);
+            } else {
+                try {
+                    map.put(key, 2 * key);
+                    shouldThrow();
+                } catch (IllegalArgumentException success) {}
+            }
+        }
+    }
+
+    void put(NavigableMap<Integer, Integer> map, int key) {
+        if (map.put(key, 2 * key) == null)
+            bs.set(key);
+    }
+
+    void remove(NavigableMap<Integer, Integer> map, int key) {
+        if (map.remove(key) != null)
+            bs.clear(key);
+    }
+
+    void bashSubMap(NavigableMap<Integer, Integer> map,
+                    int min, int max, boolean ascending) {
+        check(map, min, max, ascending);
+        check(map.descendingMap(), min, max, !ascending);
+
+        mutateSubMap(map, min, max);
+        check(map, min, max, ascending);
+        check(map.descendingMap(), min, max, !ascending);
+
+        // Recurse
+        if (max - min < 2)
+            return;
+        int midPoint = (min + max) / 2;
+
+        // headMap - pick direction and endpoint inclusion randomly
+        boolean incl = rnd.nextBoolean();
+        NavigableMap<Integer,Integer> hm = map.headMap(midPoint, incl);
+        if (ascending) {
+            if (rnd.nextBoolean())
+                bashSubMap(hm, min, midPoint - (incl ? 0 : 1), true);
+            else
+                bashSubMap(hm.descendingMap(), min, midPoint - (incl ? 0 : 1),
+                           false);
+        } else {
+            if (rnd.nextBoolean())
+                bashSubMap(hm, midPoint + (incl ? 0 : 1), max, false);
+            else
+                bashSubMap(hm.descendingMap(), midPoint + (incl ? 0 : 1), max,
+                           true);
+        }
+
+        // tailMap - pick direction and endpoint inclusion randomly
+        incl = rnd.nextBoolean();
+        NavigableMap<Integer,Integer> tm = map.tailMap(midPoint,incl);
+        if (ascending) {
+            if (rnd.nextBoolean())
+                bashSubMap(tm, midPoint + (incl ? 0 : 1), max, true);
+            else
+                bashSubMap(tm.descendingMap(), midPoint + (incl ? 0 : 1), max,
+                           false);
+        } else {
+            if (rnd.nextBoolean()) {
+                bashSubMap(tm, min, midPoint - (incl ? 0 : 1), false);
+            } else {
+                bashSubMap(tm.descendingMap(), min, midPoint - (incl ? 0 : 1),
+                           true);
+            }
+        }
+
+        // subMap - pick direction and endpoint inclusion randomly
+        int rangeSize = max - min + 1;
+        int[] endpoints = new int[2];
+        endpoints[0] = min + rnd.nextInt(rangeSize);
+        endpoints[1] = min + rnd.nextInt(rangeSize);
+        Arrays.sort(endpoints);
+        boolean lowIncl = rnd.nextBoolean();
+        boolean highIncl = rnd.nextBoolean();
+        if (ascending) {
+            NavigableMap<Integer,Integer> sm = map.subMap(
+                endpoints[0], lowIncl, endpoints[1], highIncl);
+            if (rnd.nextBoolean())
+                bashSubMap(sm, endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), true);
+            else
+                bashSubMap(sm.descendingMap(), endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), false);
+        } else {
+            NavigableMap<Integer,Integer> sm = map.subMap(
+                endpoints[1], highIncl, endpoints[0], lowIncl);
+            if (rnd.nextBoolean())
+                bashSubMap(sm, endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), false);
+            else
+                bashSubMap(sm.descendingMap(), endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), true);
+        }
+    }
+
+    /**
+     * min and max are both inclusive.  If max < min, interval is empty.
+     */
+    void check(NavigableMap<Integer, Integer> map,
+                      final int min, final int max, final boolean ascending) {
+        class ReferenceSet {
+            int lower(int key) {
+                return ascending ? lowerAscending(key) : higherAscending(key);
+            }
+            int floor(int key) {
+                return ascending ? floorAscending(key) : ceilingAscending(key);
+            }
+            int ceiling(int key) {
+                return ascending ? ceilingAscending(key) : floorAscending(key);
+            }
+            int higher(int key) {
+                return ascending ? higherAscending(key) : lowerAscending(key);
+            }
+            int first() {
+                return ascending ? firstAscending() : lastAscending();
+            }
+            int last() {
+                return ascending ? lastAscending() : firstAscending();
+            }
+            int lowerAscending(int key) {
+                return floorAscending(key - 1);
+            }
+            int floorAscending(int key) {
+                if (key < min)
+                    return -1;
+                else if (key > max)
+                    key = max;
+
+                // BitSet should support this! Test would run much faster
+                while (key >= min) {
+                    if (bs.get(key))
+                        return key;
+                    key--;
+                }
+                return -1;
+            }
+            int ceilingAscending(int key) {
+                if (key < min)
+                    key = min;
+                else if (key > max)
+                    return -1;
+                int result = bs.nextSetBit(key);
+                return result > max ? -1 : result;
+            }
+            int higherAscending(int key) {
+                return ceilingAscending(key + 1);
+            }
+            private int firstAscending() {
+                int result = ceilingAscending(min);
+                return result > max ? -1 : result;
+            }
+            private int lastAscending() {
+                int result = floorAscending(max);
+                return result < min ? -1 : result;
+            }
+        }
+        ReferenceSet rs = new ReferenceSet();
+
+        // Test contents using containsKey
+        int size = 0;
+        for (int i = min; i <= max; i++) {
+            boolean bsContainsI = bs.get(i);
+            assertEquals(bsContainsI, map.containsKey(i));
+            if (bsContainsI)
+                size++;
+        }
+        assertEquals(size, map.size());
+
+        // Test contents using contains keySet iterator
+        int size2 = 0;
+        int previousKey = -1;
+        for (int key : map.keySet()) {
+            assertTrue(bs.get(key));
+            size2++;
+            assertTrue(previousKey < 0 ||
+                (ascending ? key - previousKey > 0 : key - previousKey < 0));
+            previousKey = key;
+        }
+        assertEquals(size2, size);
+
+        // Test navigation ops
+        for (int key = min - 1; key <= max + 1; key++) {
+            assertEq(map.lowerKey(key), rs.lower(key));
+            assertEq(map.floorKey(key), rs.floor(key));
+            assertEq(map.higherKey(key), rs.higher(key));
+            assertEq(map.ceilingKey(key), rs.ceiling(key));
+        }
+
+        // Test extrema
+        if (map.size() != 0) {
+            assertEq(map.firstKey(), rs.first());
+            assertEq(map.lastKey(), rs.last());
+        } else {
+            assertEq(rs.first(), -1);
+            assertEq(rs.last(),  -1);
+            try {
+                map.firstKey();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+            try {
+                map.lastKey();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+        }
+    }
+
+    static void assertEq(Integer i, int j) {
+        if (i == null)
+            assertEquals(j, -1);
+        else
+            assertEquals((int) i, j);
+    }
+
+    static boolean eq(Integer i, int j) {
+        return (i == null) ? j == -1 : i == j;
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/ConcurrentSkipListSetTest.java b/jdk/test/java/util/concurrent/tck/ConcurrentSkipListSetTest.java
new file mode 100644
index 00000000000..9012417716c
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/ConcurrentSkipListSetTest.java
@@ -0,0 +1,1007 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.NavigableSet;
+import java.util.NoSuchElementException;
+import java.util.Random;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.concurrent.ConcurrentSkipListSet;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ConcurrentSkipListSetTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ConcurrentSkipListSetTest.class);
+    }
+
+    static class MyReverseComparator implements Comparator {
+        public int compare(Object x, Object y) {
+            return ((Comparable)y).compareTo(x);
+        }
+    }
+
+    /**
+     * Returns a new set of given size containing consecutive
+     * Integers 0 ... n.
+     */
+    private ConcurrentSkipListSet<Integer> populatedSet(int n) {
+        ConcurrentSkipListSet<Integer> q =
+            new ConcurrentSkipListSet<Integer>();
+        assertTrue(q.isEmpty());
+        for (int i = n - 1; i >= 0; i -= 2)
+            assertTrue(q.add(new Integer(i)));
+        for (int i = (n & 1); i < n; i += 2)
+            assertTrue(q.add(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(n, q.size());
+        return q;
+    }
+
+    /**
+     * Returns a new set of first 5 ints.
+     */
+    private ConcurrentSkipListSet set5() {
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        assertTrue(q.isEmpty());
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        q.add(four);
+        q.add(five);
+        assertEquals(5, q.size());
+        return q;
+    }
+
+    /**
+     * A new set has unbounded capacity
+     */
+    public void testConstructor1() {
+        assertEquals(0, new ConcurrentSkipListSet().size());
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            new ConcurrentSkipListSet((Collection)null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NPE
+     */
+    public void testConstructor4() {
+        try {
+            new ConcurrentSkipListSet(Arrays.asList(new Integer[SIZE]));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws NPE
+     */
+    public void testConstructor5() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            new ConcurrentSkipListSet(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Set contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.pollFirst());
+    }
+
+    /**
+     * The comparator used in constructor is used
+     */
+    public void testConstructor7() {
+        MyReverseComparator cmp = new MyReverseComparator();
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet(cmp);
+        assertEquals(cmp, q.comparator());
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        q.addAll(Arrays.asList(ints));
+        for (int i = SIZE - 1; i >= 0; --i)
+            assertEquals(ints[i], q.pollFirst());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        assertTrue(q.isEmpty());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.add(new Integer(2));
+        q.pollFirst();
+        q.pollFirst();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testSize() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i, q.size());
+            q.pollFirst();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * add(null) throws NPE
+     */
+    public void testAddNull() {
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        try {
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Add of comparable element succeeds
+     */
+    public void testAdd() {
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        assertTrue(q.add(zero));
+        assertTrue(q.add(one));
+    }
+
+    /**
+     * Add of duplicate element fails
+     */
+    public void testAddDup() {
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        assertTrue(q.add(zero));
+        assertFalse(q.add(zero));
+    }
+
+    /**
+     * Add of non-Comparable throws CCE
+     */
+    public void testAddNonComparable() {
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        try {
+            q.add(new Object());
+            q.add(new Object());
+            shouldThrow();
+        } catch (ClassCastException success) {}
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        try {
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testAddAll2() {
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        Integer[] ints = new Integer[SIZE];
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Set contains all elements of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(SIZE - 1 - i);
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(i, q.pollFirst());
+    }
+
+    /**
+     * pollFirst succeeds unless empty
+     */
+    public void testPollFirst() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * pollLast succeeds unless empty
+     */
+    public void testPollLast() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.pollLast());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testRemoveElement() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertTrue(q.contains(i - 1));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertFalse(q.remove(i + 1));
+            assertFalse(q.contains(i + 1));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.pollFirst();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        ConcurrentSkipListSet p = new ConcurrentSkipListSet();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        ConcurrentSkipListSet p = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.pollFirst();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            ConcurrentSkipListSet q = populatedSet(SIZE);
+            ConcurrentSkipListSet p = populatedSet(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer x = (Integer)(p.pollFirst());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    /**
+     * lower returns preceding element
+     */
+    public void testLower() {
+        ConcurrentSkipListSet q = set5();
+        Object e1 = q.lower(three);
+        assertEquals(two, e1);
+
+        Object e2 = q.lower(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.lower(one);
+        assertNull(e3);
+
+        Object e4 = q.lower(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higher returns next element
+     */
+    public void testHigher() {
+        ConcurrentSkipListSet q = set5();
+        Object e1 = q.higher(three);
+        assertEquals(four, e1);
+
+        Object e2 = q.higher(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.higher(five);
+        assertNull(e3);
+
+        Object e4 = q.higher(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floor returns preceding element
+     */
+    public void testFloor() {
+        ConcurrentSkipListSet q = set5();
+        Object e1 = q.floor(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.floor(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.floor(one);
+        assertEquals(one, e3);
+
+        Object e4 = q.floor(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceiling returns next element
+     */
+    public void testCeiling() {
+        ConcurrentSkipListSet q = set5();
+        Object e1 = q.ceiling(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.ceiling(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.ceiling(five);
+        assertEquals(five, e3);
+
+        Object e4 = q.ceiling(six);
+        assertNull(e4);
+    }
+
+    /**
+     * toArray contains all elements in sorted order
+     */
+    public void testToArray() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.pollFirst());
+    }
+
+    /**
+     * toArray(a) contains all elements in sorted order
+     */
+    public void testToArray2() {
+        ConcurrentSkipListSet<Integer> q = populatedSet(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        assertSame(ints, q.toArray(ints));
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.pollFirst());
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        Iterator it = q.iterator();
+        int i;
+        for (i = 0; it.hasNext(); i++)
+            assertTrue(q.contains(it.next()));
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty set has no elements
+     */
+    public void testEmptyIterator() {
+        NavigableSet s = new ConcurrentSkipListSet();
+        assertIteratorExhausted(s.iterator());
+        assertIteratorExhausted(s.descendingSet().iterator());
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        q.add(new Integer(2));
+        q.add(new Integer(1));
+        q.add(new Integer(3));
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertEquals(it.next(), new Integer(2));
+        assertEquals(it.next(), new Integer(3));
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized set has same elements
+     */
+    public void testSerialization() throws Exception {
+        NavigableSet x = populatedSet(SIZE);
+        NavigableSet y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x, y);
+        assertEquals(y, x);
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.pollFirst(), y.pollFirst());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * subSet returns set with keys in requested range
+     */
+    public void testSubSetContents() {
+        ConcurrentSkipListSet set = set5();
+        SortedSet sm = set.subSet(two, four);
+        assertEquals(two, sm.first());
+        assertEquals(three, sm.last());
+        assertEquals(2, sm.size());
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(two));
+        assertEquals(4, set.size());
+        assertEquals(1, sm.size());
+        assertEquals(three, sm.first());
+        assertEquals(three, sm.last());
+        assertTrue(sm.remove(three));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, set.size());
+    }
+
+    public void testSubSetContents2() {
+        ConcurrentSkipListSet set = set5();
+        SortedSet sm = set.subSet(two, three);
+        assertEquals(1, sm.size());
+        assertEquals(two, sm.first());
+        assertEquals(two, sm.last());
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertFalse(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(two));
+        assertEquals(4, set.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertFalse(sm.remove(three));
+        assertEquals(4, set.size());
+    }
+
+    /**
+     * headSet returns set with keys in requested range
+     */
+    public void testHeadSetContents() {
+        ConcurrentSkipListSet set = set5();
+        SortedSet sm = set.headSet(four);
+        assertTrue(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(one, k);
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, set.size());
+        assertEquals(four, set.first());
+    }
+
+    /**
+     * tailSet returns set with keys in requested range
+     */
+    public void testTailSetContents() {
+        ConcurrentSkipListSet set = set5();
+        SortedSet sm = set.tailSet(two);
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertTrue(sm.contains(four));
+        assertTrue(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        k = (Integer)(i.next());
+        assertEquals(four, k);
+        k = (Integer)(i.next());
+        assertEquals(five, k);
+        assertFalse(i.hasNext());
+
+        SortedSet ssm = sm.tailSet(four);
+        assertEquals(four, ssm.first());
+        assertEquals(five, ssm.last());
+        assertTrue(ssm.remove(four));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, set.size());
+    }
+
+    Random rnd = new Random(666);
+
+    /**
+     * Subsets of subsets subdivide correctly
+     */
+    public void testRecursiveSubSets() throws Exception {
+        int setSize = expensiveTests ? 1000 : 100;
+        Class cl = ConcurrentSkipListSet.class;
+
+        NavigableSet<Integer> set = newSet(cl);
+        BitSet bs = new BitSet(setSize);
+
+        populate(set, setSize, bs);
+        check(set,                 0, setSize - 1, true, bs);
+        check(set.descendingSet(), 0, setSize - 1, false, bs);
+
+        mutateSet(set, 0, setSize - 1, bs);
+        check(set,                 0, setSize - 1, true, bs);
+        check(set.descendingSet(), 0, setSize - 1, false, bs);
+
+        bashSubSet(set.subSet(0, true, setSize, false),
+                   0, setSize - 1, true, bs);
+    }
+
+    /**
+     * addAll is idempotent
+     */
+    public void testAddAll_idempotent() throws Exception {
+        Set x = populatedSet(SIZE);
+        Set y = new ConcurrentSkipListSet(x);
+        y.addAll(x);
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    static NavigableSet<Integer> newSet(Class cl) throws Exception {
+        NavigableSet<Integer> result = (NavigableSet<Integer>) cl.newInstance();
+        assertEquals(0, result.size());
+        assertFalse(result.iterator().hasNext());
+        return result;
+    }
+
+    void populate(NavigableSet<Integer> set, int limit, BitSet bs) {
+        for (int i = 0, n = 2 * limit / 3; i < n; i++) {
+            int element = rnd.nextInt(limit);
+            put(set, element, bs);
+        }
+    }
+
+    void mutateSet(NavigableSet<Integer> set, int min, int max, BitSet bs) {
+        int size = set.size();
+        int rangeSize = max - min + 1;
+
+        // Remove a bunch of entries directly
+        for (int i = 0, n = rangeSize / 2; i < n; i++) {
+            remove(set, min - 5 + rnd.nextInt(rangeSize + 10), bs);
+        }
+
+        // Remove a bunch of entries with iterator
+        for (Iterator<Integer> it = set.iterator(); it.hasNext(); ) {
+            if (rnd.nextBoolean()) {
+                bs.clear(it.next());
+                it.remove();
+            }
+        }
+
+        // Add entries till we're back to original size
+        while (set.size() < size) {
+            int element = min + rnd.nextInt(rangeSize);
+            assertTrue(element >= min && element <= max);
+            put(set, element, bs);
+        }
+    }
+
+    void mutateSubSet(NavigableSet<Integer> set, int min, int max,
+                      BitSet bs) {
+        int size = set.size();
+        int rangeSize = max - min + 1;
+
+        // Remove a bunch of entries directly
+        for (int i = 0, n = rangeSize / 2; i < n; i++) {
+            remove(set, min - 5 + rnd.nextInt(rangeSize + 10), bs);
+        }
+
+        // Remove a bunch of entries with iterator
+        for (Iterator<Integer> it = set.iterator(); it.hasNext(); ) {
+            if (rnd.nextBoolean()) {
+                bs.clear(it.next());
+                it.remove();
+            }
+        }
+
+        // Add entries till we're back to original size
+        while (set.size() < size) {
+            int element = min - 5 + rnd.nextInt(rangeSize + 10);
+            if (element >= min && element <= max) {
+                put(set, element, bs);
+            } else {
+                try {
+                    set.add(element);
+                    shouldThrow();
+                } catch (IllegalArgumentException success) {}
+            }
+        }
+    }
+
+    void put(NavigableSet<Integer> set, int element, BitSet bs) {
+        if (set.add(element))
+            bs.set(element);
+    }
+
+    void remove(NavigableSet<Integer> set, int element, BitSet bs) {
+        if (set.remove(element))
+            bs.clear(element);
+    }
+
+    void bashSubSet(NavigableSet<Integer> set,
+                    int min, int max, boolean ascending,
+                    BitSet bs) {
+        check(set, min, max, ascending, bs);
+        check(set.descendingSet(), min, max, !ascending, bs);
+
+        mutateSubSet(set, min, max, bs);
+        check(set, min, max, ascending, bs);
+        check(set.descendingSet(), min, max, !ascending, bs);
+
+        // Recurse
+        if (max - min < 2)
+            return;
+        int midPoint = (min + max) / 2;
+
+        // headSet - pick direction and endpoint inclusion randomly
+        boolean incl = rnd.nextBoolean();
+        NavigableSet<Integer> hm = set.headSet(midPoint, incl);
+        if (ascending) {
+            if (rnd.nextBoolean())
+                bashSubSet(hm, min, midPoint - (incl ? 0 : 1), true, bs);
+            else
+                bashSubSet(hm.descendingSet(), min, midPoint - (incl ? 0 : 1),
+                           false, bs);
+        } else {
+            if (rnd.nextBoolean())
+                bashSubSet(hm, midPoint + (incl ? 0 : 1), max, false, bs);
+            else
+                bashSubSet(hm.descendingSet(), midPoint + (incl ? 0 : 1), max,
+                           true, bs);
+        }
+
+        // tailSet - pick direction and endpoint inclusion randomly
+        incl = rnd.nextBoolean();
+        NavigableSet<Integer> tm = set.tailSet(midPoint,incl);
+        if (ascending) {
+            if (rnd.nextBoolean())
+                bashSubSet(tm, midPoint + (incl ? 0 : 1), max, true, bs);
+            else
+                bashSubSet(tm.descendingSet(), midPoint + (incl ? 0 : 1), max,
+                           false, bs);
+        } else {
+            if (rnd.nextBoolean()) {
+                bashSubSet(tm, min, midPoint - (incl ? 0 : 1), false, bs);
+            } else {
+                bashSubSet(tm.descendingSet(), min, midPoint - (incl ? 0 : 1),
+                           true, bs);
+            }
+        }
+
+        // subSet - pick direction and endpoint inclusion randomly
+        int rangeSize = max - min + 1;
+        int[] endpoints = new int[2];
+        endpoints[0] = min + rnd.nextInt(rangeSize);
+        endpoints[1] = min + rnd.nextInt(rangeSize);
+        Arrays.sort(endpoints);
+        boolean lowIncl = rnd.nextBoolean();
+        boolean highIncl = rnd.nextBoolean();
+        if (ascending) {
+            NavigableSet<Integer> sm = set.subSet(
+                endpoints[0], lowIncl, endpoints[1], highIncl);
+            if (rnd.nextBoolean())
+                bashSubSet(sm, endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), true, bs);
+            else
+                bashSubSet(sm.descendingSet(), endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), false, bs);
+        } else {
+            NavigableSet<Integer> sm = set.subSet(
+                endpoints[1], highIncl, endpoints[0], lowIncl);
+            if (rnd.nextBoolean())
+                bashSubSet(sm, endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), false, bs);
+            else
+                bashSubSet(sm.descendingSet(), endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), true, bs);
+        }
+    }
+
+    /**
+     * min and max are both inclusive.  If max < min, interval is empty.
+     */
+    void check(NavigableSet<Integer> set,
+               final int min, final int max, final boolean ascending,
+               final BitSet bs) {
+        class ReferenceSet {
+            int lower(int element) {
+                return ascending ?
+                    lowerAscending(element) : higherAscending(element);
+            }
+            int floor(int element) {
+                return ascending ?
+                    floorAscending(element) : ceilingAscending(element);
+            }
+            int ceiling(int element) {
+                return ascending ?
+                    ceilingAscending(element) : floorAscending(element);
+            }
+            int higher(int element) {
+                return ascending ?
+                    higherAscending(element) : lowerAscending(element);
+            }
+            int first() {
+                return ascending ? firstAscending() : lastAscending();
+            }
+            int last() {
+                return ascending ? lastAscending() : firstAscending();
+            }
+            int lowerAscending(int element) {
+                return floorAscending(element - 1);
+            }
+            int floorAscending(int element) {
+                if (element < min)
+                    return -1;
+                else if (element > max)
+                    element = max;
+
+                // BitSet should support this! Test would run much faster
+                while (element >= min) {
+                    if (bs.get(element))
+                        return element;
+                    element--;
+                }
+                return -1;
+            }
+            int ceilingAscending(int element) {
+                if (element < min)
+                    element = min;
+                else if (element > max)
+                    return -1;
+                int result = bs.nextSetBit(element);
+                return result > max ? -1 : result;
+            }
+            int higherAscending(int element) {
+                return ceilingAscending(element + 1);
+            }
+            private int firstAscending() {
+                int result = ceilingAscending(min);
+                return result > max ? -1 : result;
+            }
+            private int lastAscending() {
+                int result = floorAscending(max);
+                return result < min ? -1 : result;
+            }
+        }
+        ReferenceSet rs = new ReferenceSet();
+
+        // Test contents using containsElement
+        int size = 0;
+        for (int i = min; i <= max; i++) {
+            boolean bsContainsI = bs.get(i);
+            assertEquals(bsContainsI, set.contains(i));
+            if (bsContainsI)
+                size++;
+        }
+        assertEquals(size, set.size());
+
+        // Test contents using contains elementSet iterator
+        int size2 = 0;
+        int previousElement = -1;
+        for (int element : set) {
+            assertTrue(bs.get(element));
+            size2++;
+            assertTrue(previousElement < 0 || (ascending ?
+                element - previousElement > 0 : element - previousElement < 0));
+            previousElement = element;
+        }
+        assertEquals(size2, size);
+
+        // Test navigation ops
+        for (int element = min - 1; element <= max + 1; element++) {
+            assertEq(set.lower(element), rs.lower(element));
+            assertEq(set.floor(element), rs.floor(element));
+            assertEq(set.higher(element), rs.higher(element));
+            assertEq(set.ceiling(element), rs.ceiling(element));
+        }
+
+        // Test extrema
+        if (set.size() != 0) {
+            assertEq(set.first(), rs.first());
+            assertEq(set.last(), rs.last());
+        } else {
+            assertEq(rs.first(), -1);
+            assertEq(rs.last(),  -1);
+            try {
+                set.first();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+            try {
+                set.last();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+        }
+    }
+
+    static void assertEq(Integer i, int j) {
+        if (i == null)
+            assertEquals(j, -1);
+        else
+            assertEquals((int) i, j);
+    }
+
+    static boolean eq(Integer i, int j) {
+        return (i == null) ? j == -1 : i == j;
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/ConcurrentSkipListSubMapTest.java b/jdk/test/java/util/concurrent/tck/ConcurrentSkipListSubMapTest.java
new file mode 100644
index 00000000000..ddc82f78a56
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/ConcurrentSkipListSubMapTest.java
@@ -0,0 +1,1450 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.concurrent.ConcurrentNavigableMap;
+import java.util.concurrent.ConcurrentSkipListMap;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ConcurrentSkipListSubMapTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ConcurrentSkipListSubMapTest.class);
+    }
+
+    /**
+     * Returns a new map from Integers 1-5 to Strings "A"-"E".
+     */
+    private static ConcurrentNavigableMap map5() {
+        ConcurrentSkipListMap map = new ConcurrentSkipListMap();
+        assertTrue(map.isEmpty());
+        map.put(zero, "Z");
+        map.put(one, "A");
+        map.put(five, "E");
+        map.put(three, "C");
+        map.put(two, "B");
+        map.put(four, "D");
+        map.put(seven, "F");
+        assertFalse(map.isEmpty());
+        assertEquals(7, map.size());
+        return map.subMap(one, true, seven, false);
+    }
+
+    /**
+     * Returns a new map from Integers -5 to -1 to Strings "A"-"E".
+     */
+    private static ConcurrentNavigableMap dmap5() {
+        ConcurrentSkipListMap map = new ConcurrentSkipListMap();
+        assertTrue(map.isEmpty());
+        map.put(m1, "A");
+        map.put(m5, "E");
+        map.put(m3, "C");
+        map.put(m2, "B");
+        map.put(m4, "D");
+        assertFalse(map.isEmpty());
+        assertEquals(5, map.size());
+        return map.descendingMap();
+    }
+
+    private static ConcurrentNavigableMap map0() {
+        ConcurrentSkipListMap map = new ConcurrentSkipListMap();
+        assertTrue(map.isEmpty());
+        return map.tailMap(one, true);
+    }
+
+    private static ConcurrentNavigableMap dmap0() {
+        ConcurrentSkipListMap map = new ConcurrentSkipListMap();
+        assertTrue(map.isEmpty());
+        return map;
+    }
+
+    /**
+     * clear removes all pairs
+     */
+    public void testClear() {
+        ConcurrentNavigableMap map = map5();
+        map.clear();
+        assertEquals(0, map.size());
+    }
+
+    /**
+     * Maps with same contents are equal
+     */
+    public void testEquals() {
+        ConcurrentNavigableMap map1 = map5();
+        ConcurrentNavigableMap map2 = map5();
+        assertEquals(map1, map2);
+        assertEquals(map2, map1);
+        map1.clear();
+        assertFalse(map1.equals(map2));
+        assertFalse(map2.equals(map1));
+    }
+
+    /**
+     * containsKey returns true for contained key
+     */
+    public void testContainsKey() {
+        ConcurrentNavigableMap map = map5();
+        assertTrue(map.containsKey(one));
+        assertFalse(map.containsKey(zero));
+    }
+
+    /**
+     * containsValue returns true for held values
+     */
+    public void testContainsValue() {
+        ConcurrentNavigableMap map = map5();
+        assertTrue(map.containsValue("A"));
+        assertFalse(map.containsValue("Z"));
+    }
+
+    /**
+     * get returns the correct element at the given key,
+     * or null if not present
+     */
+    public void testGet() {
+        ConcurrentNavigableMap map = map5();
+        assertEquals("A", (String)map.get(one));
+        ConcurrentNavigableMap empty = map0();
+        assertNull(empty.get(one));
+    }
+
+    /**
+     * isEmpty is true of empty map and false for non-empty
+     */
+    public void testIsEmpty() {
+        ConcurrentNavigableMap empty = map0();
+        ConcurrentNavigableMap map = map5();
+        assertTrue(empty.isEmpty());
+        assertFalse(map.isEmpty());
+    }
+
+    /**
+     * firstKey returns first key
+     */
+    public void testFirstKey() {
+        ConcurrentNavigableMap map = map5();
+        assertEquals(one, map.firstKey());
+    }
+
+    /**
+     * lastKey returns last key
+     */
+    public void testLastKey() {
+        ConcurrentNavigableMap map = map5();
+        assertEquals(five, map.lastKey());
+    }
+
+    /**
+     * keySet returns a Set containing all the keys
+     */
+    public void testKeySet() {
+        ConcurrentNavigableMap map = map5();
+        Set s = map.keySet();
+        assertEquals(5, s.size());
+        assertTrue(s.contains(one));
+        assertTrue(s.contains(two));
+        assertTrue(s.contains(three));
+        assertTrue(s.contains(four));
+        assertTrue(s.contains(five));
+    }
+
+    /**
+     * keySet is ordered
+     */
+    public void testKeySetOrder() {
+        ConcurrentNavigableMap map = map5();
+        Set s = map.keySet();
+        Iterator i = s.iterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, one);
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) < 0);
+            last = k;
+        }
+    }
+
+    /**
+     * values collection contains all values
+     */
+    public void testValues() {
+        ConcurrentNavigableMap map = map5();
+        Collection s = map.values();
+        assertEquals(5, s.size());
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * keySet.toArray returns contains all keys
+     */
+    public void testKeySetToArray() {
+        ConcurrentNavigableMap map = map5();
+        Set s = map.keySet();
+        Object[] ar = s.toArray();
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        assertEquals(5, ar.length);
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * descendingkeySet.toArray returns contains all keys
+     */
+    public void testDescendingKeySetToArray() {
+        ConcurrentNavigableMap map = map5();
+        Set s = map.descendingKeySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * Values.toArray contains all values
+     */
+    public void testValuesToArray() {
+        ConcurrentNavigableMap map = map5();
+        Collection v = map.values();
+        Object[] ar = v.toArray();
+        ArrayList s = new ArrayList(Arrays.asList(ar));
+        assertEquals(5, ar.length);
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * entrySet contains all pairs
+     */
+    public void testEntrySet() {
+        ConcurrentNavigableMap map = map5();
+        Set s = map.entrySet();
+        assertEquals(5, s.size());
+        Iterator it = s.iterator();
+        while (it.hasNext()) {
+            Map.Entry e = (Map.Entry) it.next();
+            assertTrue(
+                       (e.getKey().equals(one) && e.getValue().equals("A")) ||
+                       (e.getKey().equals(two) && e.getValue().equals("B")) ||
+                       (e.getKey().equals(three) && e.getValue().equals("C")) ||
+                       (e.getKey().equals(four) && e.getValue().equals("D")) ||
+                       (e.getKey().equals(five) && e.getValue().equals("E")));
+        }
+    }
+
+    /**
+     * putAll adds all key-value pairs from the given map
+     */
+    public void testPutAll() {
+        ConcurrentNavigableMap empty = map0();
+        ConcurrentNavigableMap map = map5();
+        empty.putAll(map);
+        assertEquals(5, empty.size());
+        assertTrue(empty.containsKey(one));
+        assertTrue(empty.containsKey(two));
+        assertTrue(empty.containsKey(three));
+        assertTrue(empty.containsKey(four));
+        assertTrue(empty.containsKey(five));
+    }
+
+    /**
+     * putIfAbsent works when the given key is not present
+     */
+    public void testPutIfAbsent() {
+        ConcurrentNavigableMap map = map5();
+        map.putIfAbsent(six, "Z");
+        assertTrue(map.containsKey(six));
+    }
+
+    /**
+     * putIfAbsent does not add the pair if the key is already present
+     */
+    public void testPutIfAbsent2() {
+        ConcurrentNavigableMap map = map5();
+        assertEquals("A", map.putIfAbsent(one, "Z"));
+    }
+
+    /**
+     * replace fails when the given key is not present
+     */
+    public void testReplace() {
+        ConcurrentNavigableMap map = map5();
+        assertNull(map.replace(six, "Z"));
+        assertFalse(map.containsKey(six));
+    }
+
+    /**
+     * replace succeeds if the key is already present
+     */
+    public void testReplace2() {
+        ConcurrentNavigableMap map = map5();
+        assertNotNull(map.replace(one, "Z"));
+        assertEquals("Z", map.get(one));
+    }
+
+    /**
+     * replace value fails when the given key not mapped to expected value
+     */
+    public void testReplaceValue() {
+        ConcurrentNavigableMap map = map5();
+        assertEquals("A", map.get(one));
+        assertFalse(map.replace(one, "Z", "Z"));
+        assertEquals("A", map.get(one));
+    }
+
+    /**
+     * replace value succeeds when the given key mapped to expected value
+     */
+    public void testReplaceValue2() {
+        ConcurrentNavigableMap map = map5();
+        assertEquals("A", map.get(one));
+        assertTrue(map.replace(one, "A", "Z"));
+        assertEquals("Z", map.get(one));
+    }
+
+    /**
+     * remove removes the correct key-value pair from the map
+     */
+    public void testRemove() {
+        ConcurrentNavigableMap map = map5();
+        map.remove(five);
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(five));
+    }
+
+    /**
+     * remove(key,value) removes only if pair present
+     */
+    public void testRemove2() {
+        ConcurrentNavigableMap map = map5();
+        assertTrue(map.containsKey(five));
+        assertEquals("E", map.get(five));
+        map.remove(five, "E");
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(five));
+        map.remove(four, "A");
+        assertEquals(4, map.size());
+        assertTrue(map.containsKey(four));
+    }
+
+    /**
+     * lowerEntry returns preceding entry.
+     */
+    public void testLowerEntry() {
+        ConcurrentNavigableMap map = map5();
+        Map.Entry e1 = map.lowerEntry(three);
+        assertEquals(two, e1.getKey());
+
+        Map.Entry e2 = map.lowerEntry(six);
+        assertEquals(five, e2.getKey());
+
+        Map.Entry e3 = map.lowerEntry(one);
+        assertNull(e3);
+
+        Map.Entry e4 = map.lowerEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higherEntry returns next entry.
+     */
+    public void testHigherEntry() {
+        ConcurrentNavigableMap map = map5();
+        Map.Entry e1 = map.higherEntry(three);
+        assertEquals(four, e1.getKey());
+
+        Map.Entry e2 = map.higherEntry(zero);
+        assertEquals(one, e2.getKey());
+
+        Map.Entry e3 = map.higherEntry(five);
+        assertNull(e3);
+
+        Map.Entry e4 = map.higherEntry(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floorEntry returns preceding entry.
+     */
+    public void testFloorEntry() {
+        ConcurrentNavigableMap map = map5();
+        Map.Entry e1 = map.floorEntry(three);
+        assertEquals(three, e1.getKey());
+
+        Map.Entry e2 = map.floorEntry(six);
+        assertEquals(five, e2.getKey());
+
+        Map.Entry e3 = map.floorEntry(one);
+        assertEquals(one, e3.getKey());
+
+        Map.Entry e4 = map.floorEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceilingEntry returns next entry.
+     */
+    public void testCeilingEntry() {
+        ConcurrentNavigableMap map = map5();
+        Map.Entry e1 = map.ceilingEntry(three);
+        assertEquals(three, e1.getKey());
+
+        Map.Entry e2 = map.ceilingEntry(zero);
+        assertEquals(one, e2.getKey());
+
+        Map.Entry e3 = map.ceilingEntry(five);
+        assertEquals(five, e3.getKey());
+
+        Map.Entry e4 = map.ceilingEntry(six);
+        assertNull(e4);
+    }
+
+    /**
+     * pollFirstEntry returns entries in order
+     */
+    public void testPollFirstEntry() {
+        ConcurrentNavigableMap map = map5();
+        Map.Entry e = map.pollFirstEntry();
+        assertEquals(one, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(two, e.getKey());
+        map.put(one, "A");
+        e = map.pollFirstEntry();
+        assertEquals(one, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(three, e.getKey());
+        map.remove(four);
+        e = map.pollFirstEntry();
+        assertEquals(five, e.getKey());
+        try {
+            e.setValue("A");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollFirstEntry();
+        assertNull(e);
+    }
+
+    /**
+     * pollLastEntry returns entries in order
+     */
+    public void testPollLastEntry() {
+        ConcurrentNavigableMap map = map5();
+        Map.Entry e = map.pollLastEntry();
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(four, e.getKey());
+        map.put(five, "E");
+        e = map.pollLastEntry();
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(three, e.getKey());
+        map.remove(two);
+        e = map.pollLastEntry();
+        assertEquals(one, e.getKey());
+        try {
+            e.setValue("E");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollLastEntry();
+        assertNull(e);
+    }
+
+    /**
+     * size returns the correct values
+     */
+    public void testSize() {
+        ConcurrentNavigableMap map = map5();
+        ConcurrentNavigableMap empty = map0();
+        assertEquals(0, empty.size());
+        assertEquals(5, map.size());
+    }
+
+    /**
+     * toString contains toString of elements
+     */
+    public void testToString() {
+        ConcurrentNavigableMap map = map5();
+        String s = map.toString();
+        for (int i = 1; i <= 5; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    // Exception tests
+
+    /**
+     * get(null) of nonempty map throws NPE
+     */
+    public void testGet_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = map5();
+            c.get(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsKey(null) of nonempty map throws NPE
+     */
+    public void testContainsKey_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = map5();
+            c.containsKey(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsValue(null) throws NPE
+     */
+    public void testContainsValue_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = map0();
+            c.containsValue(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * put(null,x) throws NPE
+     */
+    public void testPut1_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = map5();
+            c.put(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * putIfAbsent(null, x) throws NPE
+     */
+    public void testPutIfAbsent1_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = map5();
+            c.putIfAbsent(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(null, x) throws NPE
+     */
+    public void testReplace_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = map5();
+            c.replace(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(null, x, y) throws NPE
+     */
+    public void testReplaceValue_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = map5();
+            c.replace(null, one, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null) throws NPE
+     */
+    public void testRemove1_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = map5();
+            c.remove(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null, x) throws NPE
+     */
+    public void testRemove2_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = map5();
+            c.remove(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * A deserialized map equals original
+     */
+    public void testSerialization() throws Exception {
+        NavigableMap x = map5();
+        NavigableMap y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    /**
+     * subMap returns map with keys in requested range
+     */
+    public void testSubMapContents() {
+        ConcurrentNavigableMap map = map5();
+        SortedMap sm = map.subMap(two, four);
+        assertEquals(two, sm.firstKey());
+        assertEquals(three, sm.lastKey());
+        assertEquals(2, sm.size());
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(two));
+        assertEquals(4, map.size());
+        assertEquals(1, sm.size());
+        assertEquals(three, sm.firstKey());
+        assertEquals(three, sm.lastKey());
+        assertEquals("C", sm.remove(three));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, map.size());
+    }
+
+    public void testSubMapContents2() {
+        ConcurrentNavigableMap map = map5();
+        SortedMap sm = map.subMap(two, three);
+        assertEquals(1, sm.size());
+        assertEquals(two, sm.firstKey());
+        assertEquals(two, sm.lastKey());
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertFalse(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(two));
+        assertEquals(4, map.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertSame(sm.remove(three), null);
+        assertEquals(4, map.size());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testHeadMapContents() {
+        ConcurrentNavigableMap map = map5();
+        SortedMap sm = map.headMap(four);
+        assertTrue(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(one, k);
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, map.size());
+        assertEquals(four, map.firstKey());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testTailMapContents() {
+        ConcurrentNavigableMap map = map5();
+        SortedMap sm = map.tailMap(two);
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertTrue(sm.containsKey(four));
+        assertTrue(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        k = (Integer)(i.next());
+        assertEquals(four, k);
+        k = (Integer)(i.next());
+        assertEquals(five, k);
+        assertFalse(i.hasNext());
+
+        Iterator ei = sm.entrySet().iterator();
+        Map.Entry e;
+        e = (Map.Entry)(ei.next());
+        assertEquals(two, e.getKey());
+        assertEquals("B", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(three, e.getKey());
+        assertEquals("C", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(four, e.getKey());
+        assertEquals("D", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        assertFalse(i.hasNext());
+
+        SortedMap ssm = sm.tailMap(four);
+        assertEquals(four, ssm.firstKey());
+        assertEquals(five, ssm.lastKey());
+        assertEquals("D", ssm.remove(four));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, map.size());
+    }
+
+    /**
+     * clear removes all pairs
+     */
+    public void testDescendingClear() {
+        ConcurrentNavigableMap map = dmap5();
+        map.clear();
+        assertEquals(0, map.size());
+    }
+
+    /**
+     * Maps with same contents are equal
+     */
+    public void testDescendingEquals() {
+        ConcurrentNavigableMap map1 = dmap5();
+        ConcurrentNavigableMap map2 = dmap5();
+        assertEquals(map1, map2);
+        assertEquals(map2, map1);
+        map1.clear();
+        assertFalse(map1.equals(map2));
+        assertFalse(map2.equals(map1));
+    }
+
+    /**
+     * containsKey returns true for contained key
+     */
+    public void testDescendingContainsKey() {
+        ConcurrentNavigableMap map = dmap5();
+        assertTrue(map.containsKey(m1));
+        assertFalse(map.containsKey(zero));
+    }
+
+    /**
+     * containsValue returns true for held values
+     */
+    public void testDescendingContainsValue() {
+        ConcurrentNavigableMap map = dmap5();
+        assertTrue(map.containsValue("A"));
+        assertFalse(map.containsValue("Z"));
+    }
+
+    /**
+     * get returns the correct element at the given key,
+     * or null if not present
+     */
+    public void testDescendingGet() {
+        ConcurrentNavigableMap map = dmap5();
+        assertEquals("A", (String)map.get(m1));
+        ConcurrentNavigableMap empty = dmap0();
+        assertNull(empty.get(m1));
+    }
+
+    /**
+     * isEmpty is true of empty map and false for non-empty
+     */
+    public void testDescendingIsEmpty() {
+        ConcurrentNavigableMap empty = dmap0();
+        ConcurrentNavigableMap map = dmap5();
+        assertTrue(empty.isEmpty());
+        assertFalse(map.isEmpty());
+    }
+
+    /**
+     * firstKey returns first key
+     */
+    public void testDescendingFirstKey() {
+        ConcurrentNavigableMap map = dmap5();
+        assertEquals(m1, map.firstKey());
+    }
+
+    /**
+     * lastKey returns last key
+     */
+    public void testDescendingLastKey() {
+        ConcurrentNavigableMap map = dmap5();
+        assertEquals(m5, map.lastKey());
+    }
+
+    /**
+     * keySet returns a Set containing all the keys
+     */
+    public void testDescendingKeySet() {
+        ConcurrentNavigableMap map = dmap5();
+        Set s = map.keySet();
+        assertEquals(5, s.size());
+        assertTrue(s.contains(m1));
+        assertTrue(s.contains(m2));
+        assertTrue(s.contains(m3));
+        assertTrue(s.contains(m4));
+        assertTrue(s.contains(m5));
+    }
+
+    /**
+     * keySet is ordered
+     */
+    public void testDescendingKeySetOrder() {
+        ConcurrentNavigableMap map = dmap5();
+        Set s = map.keySet();
+        Iterator i = s.iterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, m1);
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) > 0);
+            last = k;
+        }
+    }
+
+    /**
+     * values collection contains all values
+     */
+    public void testDescendingValues() {
+        ConcurrentNavigableMap map = dmap5();
+        Collection s = map.values();
+        assertEquals(5, s.size());
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * keySet.toArray returns contains all keys
+     */
+    public void testDescendingAscendingKeySetToArray() {
+        ConcurrentNavigableMap map = dmap5();
+        Set s = map.keySet();
+        Object[] ar = s.toArray();
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        assertEquals(5, ar.length);
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * descendingkeySet.toArray returns contains all keys
+     */
+    public void testDescendingDescendingKeySetToArray() {
+        ConcurrentNavigableMap map = dmap5();
+        Set s = map.descendingKeySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * Values.toArray contains all values
+     */
+    public void testDescendingValuesToArray() {
+        ConcurrentNavigableMap map = dmap5();
+        Collection v = map.values();
+        Object[] ar = v.toArray();
+        ArrayList s = new ArrayList(Arrays.asList(ar));
+        assertEquals(5, ar.length);
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * entrySet contains all pairs
+     */
+    public void testDescendingEntrySet() {
+        ConcurrentNavigableMap map = dmap5();
+        Set s = map.entrySet();
+        assertEquals(5, s.size());
+        Iterator it = s.iterator();
+        while (it.hasNext()) {
+            Map.Entry e = (Map.Entry) it.next();
+            assertTrue(
+                       (e.getKey().equals(m1) && e.getValue().equals("A")) ||
+                       (e.getKey().equals(m2) && e.getValue().equals("B")) ||
+                       (e.getKey().equals(m3) && e.getValue().equals("C")) ||
+                       (e.getKey().equals(m4) && e.getValue().equals("D")) ||
+                       (e.getKey().equals(m5) && e.getValue().equals("E")));
+        }
+    }
+
+    /**
+     * putAll adds all key-value pairs from the given map
+     */
+    public void testDescendingPutAll() {
+        ConcurrentNavigableMap empty = dmap0();
+        ConcurrentNavigableMap map = dmap5();
+        empty.putAll(map);
+        assertEquals(5, empty.size());
+        assertTrue(empty.containsKey(m1));
+        assertTrue(empty.containsKey(m2));
+        assertTrue(empty.containsKey(m3));
+        assertTrue(empty.containsKey(m4));
+        assertTrue(empty.containsKey(m5));
+    }
+
+    /**
+     * putIfAbsent works when the given key is not present
+     */
+    public void testDescendingPutIfAbsent() {
+        ConcurrentNavigableMap map = dmap5();
+        map.putIfAbsent(six, "Z");
+        assertTrue(map.containsKey(six));
+    }
+
+    /**
+     * putIfAbsent does not add the pair if the key is already present
+     */
+    public void testDescendingPutIfAbsent2() {
+        ConcurrentNavigableMap map = dmap5();
+        assertEquals("A", map.putIfAbsent(m1, "Z"));
+    }
+
+    /**
+     * replace fails when the given key is not present
+     */
+    public void testDescendingReplace() {
+        ConcurrentNavigableMap map = dmap5();
+        assertNull(map.replace(six, "Z"));
+        assertFalse(map.containsKey(six));
+    }
+
+    /**
+     * replace succeeds if the key is already present
+     */
+    public void testDescendingReplace2() {
+        ConcurrentNavigableMap map = dmap5();
+        assertNotNull(map.replace(m1, "Z"));
+        assertEquals("Z", map.get(m1));
+    }
+
+    /**
+     * replace value fails when the given key not mapped to expected value
+     */
+    public void testDescendingReplaceValue() {
+        ConcurrentNavigableMap map = dmap5();
+        assertEquals("A", map.get(m1));
+        assertFalse(map.replace(m1, "Z", "Z"));
+        assertEquals("A", map.get(m1));
+    }
+
+    /**
+     * replace value succeeds when the given key mapped to expected value
+     */
+    public void testDescendingReplaceValue2() {
+        ConcurrentNavigableMap map = dmap5();
+        assertEquals("A", map.get(m1));
+        assertTrue(map.replace(m1, "A", "Z"));
+        assertEquals("Z", map.get(m1));
+    }
+
+    /**
+     * remove removes the correct key-value pair from the map
+     */
+    public void testDescendingRemove() {
+        ConcurrentNavigableMap map = dmap5();
+        map.remove(m5);
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(m5));
+    }
+
+    /**
+     * remove(key,value) removes only if pair present
+     */
+    public void testDescendingRemove2() {
+        ConcurrentNavigableMap map = dmap5();
+        assertTrue(map.containsKey(m5));
+        assertEquals("E", map.get(m5));
+        map.remove(m5, "E");
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(m5));
+        map.remove(m4, "A");
+        assertEquals(4, map.size());
+        assertTrue(map.containsKey(m4));
+    }
+
+    /**
+     * lowerEntry returns preceding entry.
+     */
+    public void testDescendingLowerEntry() {
+        ConcurrentNavigableMap map = dmap5();
+        Map.Entry e1 = map.lowerEntry(m3);
+        assertEquals(m2, e1.getKey());
+
+        Map.Entry e2 = map.lowerEntry(m6);
+        assertEquals(m5, e2.getKey());
+
+        Map.Entry e3 = map.lowerEntry(m1);
+        assertNull(e3);
+
+        Map.Entry e4 = map.lowerEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higherEntry returns next entry.
+     */
+    public void testDescendingHigherEntry() {
+        ConcurrentNavigableMap map = dmap5();
+        Map.Entry e1 = map.higherEntry(m3);
+        assertEquals(m4, e1.getKey());
+
+        Map.Entry e2 = map.higherEntry(zero);
+        assertEquals(m1, e2.getKey());
+
+        Map.Entry e3 = map.higherEntry(m5);
+        assertNull(e3);
+
+        Map.Entry e4 = map.higherEntry(m6);
+        assertNull(e4);
+    }
+
+    /**
+     * floorEntry returns preceding entry.
+     */
+    public void testDescendingFloorEntry() {
+        ConcurrentNavigableMap map = dmap5();
+        Map.Entry e1 = map.floorEntry(m3);
+        assertEquals(m3, e1.getKey());
+
+        Map.Entry e2 = map.floorEntry(m6);
+        assertEquals(m5, e2.getKey());
+
+        Map.Entry e3 = map.floorEntry(m1);
+        assertEquals(m1, e3.getKey());
+
+        Map.Entry e4 = map.floorEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceilingEntry returns next entry.
+     */
+    public void testDescendingCeilingEntry() {
+        ConcurrentNavigableMap map = dmap5();
+        Map.Entry e1 = map.ceilingEntry(m3);
+        assertEquals(m3, e1.getKey());
+
+        Map.Entry e2 = map.ceilingEntry(zero);
+        assertEquals(m1, e2.getKey());
+
+        Map.Entry e3 = map.ceilingEntry(m5);
+        assertEquals(m5, e3.getKey());
+
+        Map.Entry e4 = map.ceilingEntry(m6);
+        assertNull(e4);
+    }
+
+    /**
+     * pollFirstEntry returns entries in order
+     */
+    public void testDescendingPollFirstEntry() {
+        ConcurrentNavigableMap map = dmap5();
+        Map.Entry e = map.pollFirstEntry();
+        assertEquals(m1, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(m2, e.getKey());
+        map.put(m1, "A");
+        e = map.pollFirstEntry();
+        assertEquals(m1, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(m3, e.getKey());
+        map.remove(m4);
+        e = map.pollFirstEntry();
+        assertEquals(m5, e.getKey());
+        try {
+            e.setValue("A");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollFirstEntry();
+        assertNull(e);
+    }
+
+    /**
+     * pollLastEntry returns entries in order
+     */
+    public void testDescendingPollLastEntry() {
+        ConcurrentNavigableMap map = dmap5();
+        Map.Entry e = map.pollLastEntry();
+        assertEquals(m5, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(m4, e.getKey());
+        map.put(m5, "E");
+        e = map.pollLastEntry();
+        assertEquals(m5, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(m3, e.getKey());
+        map.remove(m2);
+        e = map.pollLastEntry();
+        assertEquals(m1, e.getKey());
+        try {
+            e.setValue("E");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollLastEntry();
+        assertNull(e);
+    }
+
+    /**
+     * size returns the correct values
+     */
+    public void testDescendingSize() {
+        ConcurrentNavigableMap map = dmap5();
+        ConcurrentNavigableMap empty = dmap0();
+        assertEquals(0, empty.size());
+        assertEquals(5, map.size());
+    }
+
+    /**
+     * toString contains toString of elements
+     */
+    public void testDescendingToString() {
+        ConcurrentNavigableMap map = dmap5();
+        String s = map.toString();
+        for (int i = 1; i <= 5; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    // Exception testDescendings
+
+    /**
+     * get(null) of empty map throws NPE
+     */
+    public void testDescendingGet_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = dmap5();
+            c.get(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsKey(null) of empty map throws NPE
+     */
+    public void testDescendingContainsKey_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = dmap5();
+            c.containsKey(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsValue(null) throws NPE
+     */
+    public void testDescendingContainsValue_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = dmap0();
+            c.containsValue(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * put(null,x) throws NPE
+     */
+    public void testDescendingPut1_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = dmap5();
+            c.put(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * putIfAbsent(null, x) throws NPE
+     */
+    public void testDescendingPutIfAbsent1_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = dmap5();
+            c.putIfAbsent(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(null, x) throws NPE
+     */
+    public void testDescendingReplace_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = dmap5();
+            c.replace(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(null, x, y) throws NPE
+     */
+    public void testDescendingReplaceValue_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = dmap5();
+            c.replace(null, m1, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null) throws NPE
+     */
+    public void testDescendingRemove1_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = dmap5();
+            c.remove(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null, x) throws NPE
+     */
+    public void testDescendingRemove2_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = dmap5();
+            c.remove(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * A deserialized map equals original
+     */
+    public void testDescendingSerialization() throws Exception {
+        NavigableMap x = dmap5();
+        NavigableMap y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    /**
+     * subMap returns map with keys in requested range
+     */
+    public void testDescendingSubMapContents() {
+        ConcurrentNavigableMap map = dmap5();
+        SortedMap sm = map.subMap(m2, m4);
+        assertEquals(m2, sm.firstKey());
+        assertEquals(m3, sm.lastKey());
+        assertEquals(2, sm.size());
+        assertFalse(sm.containsKey(m1));
+        assertTrue(sm.containsKey(m2));
+        assertTrue(sm.containsKey(m3));
+        assertFalse(sm.containsKey(m4));
+        assertFalse(sm.containsKey(m5));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(m2));
+        assertEquals(4, map.size());
+        assertEquals(1, sm.size());
+        assertEquals(m3, sm.firstKey());
+        assertEquals(m3, sm.lastKey());
+        assertEquals("C", sm.remove(m3));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, map.size());
+    }
+
+    public void testDescendingSubMapContents2() {
+        ConcurrentNavigableMap map = dmap5();
+        SortedMap sm = map.subMap(m2, m3);
+        assertEquals(1, sm.size());
+        assertEquals(m2, sm.firstKey());
+        assertEquals(m2, sm.lastKey());
+        assertFalse(sm.containsKey(m1));
+        assertTrue(sm.containsKey(m2));
+        assertFalse(sm.containsKey(m3));
+        assertFalse(sm.containsKey(m4));
+        assertFalse(sm.containsKey(m5));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(m2));
+        assertEquals(4, map.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertSame(sm.remove(m3), null);
+        assertEquals(4, map.size());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testDescendingHeadMapContents() {
+        ConcurrentNavigableMap map = dmap5();
+        SortedMap sm = map.headMap(m4);
+        assertTrue(sm.containsKey(m1));
+        assertTrue(sm.containsKey(m2));
+        assertTrue(sm.containsKey(m3));
+        assertFalse(sm.containsKey(m4));
+        assertFalse(sm.containsKey(m5));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m1, k);
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, map.size());
+        assertEquals(m4, map.firstKey());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testDescendingTailMapContents() {
+        ConcurrentNavigableMap map = dmap5();
+        SortedMap sm = map.tailMap(m2);
+        assertFalse(sm.containsKey(m1));
+        assertTrue(sm.containsKey(m2));
+        assertTrue(sm.containsKey(m3));
+        assertTrue(sm.containsKey(m4));
+        assertTrue(sm.containsKey(m5));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        k = (Integer)(i.next());
+        assertEquals(m4, k);
+        k = (Integer)(i.next());
+        assertEquals(m5, k);
+        assertFalse(i.hasNext());
+
+        Iterator ei = sm.entrySet().iterator();
+        Map.Entry e;
+        e = (Map.Entry)(ei.next());
+        assertEquals(m2, e.getKey());
+        assertEquals("B", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(m3, e.getKey());
+        assertEquals("C", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(m4, e.getKey());
+        assertEquals("D", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(m5, e.getKey());
+        assertEquals("E", e.getValue());
+        assertFalse(i.hasNext());
+
+        SortedMap ssm = sm.tailMap(m4);
+        assertEquals(m4, ssm.firstKey());
+        assertEquals(m5, ssm.lastKey());
+        assertEquals("D", ssm.remove(m4));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, map.size());
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/ConcurrentSkipListSubSetTest.java b/jdk/test/java/util/concurrent/tck/ConcurrentSkipListSubSetTest.java
new file mode 100644
index 00000000000..bad618f3fab
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/ConcurrentSkipListSubSetTest.java
@@ -0,0 +1,1141 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.NavigableSet;
+import java.util.SortedSet;
+import java.util.concurrent.ConcurrentSkipListSet;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ConcurrentSkipListSubSetTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ConcurrentSkipListSubSetTest.class);
+    }
+
+    static class MyReverseComparator implements Comparator {
+        public int compare(Object x, Object y) {
+            return ((Comparable)y).compareTo(x);
+        }
+    }
+
+    /**
+     * Returns a new set of given size containing consecutive
+     * Integers 0 ... n.
+     */
+    private NavigableSet<Integer> populatedSet(int n) {
+        ConcurrentSkipListSet<Integer> q =
+            new ConcurrentSkipListSet<Integer>();
+        assertTrue(q.isEmpty());
+
+        for (int i = n - 1; i >= 0; i -= 2)
+            assertTrue(q.add(new Integer(i)));
+        for (int i = (n & 1); i < n; i += 2)
+            assertTrue(q.add(new Integer(i)));
+        assertTrue(q.add(new Integer(-n)));
+        assertTrue(q.add(new Integer(n)));
+        NavigableSet s = q.subSet(new Integer(0), true, new Integer(n), false);
+        assertFalse(s.isEmpty());
+        assertEquals(n, s.size());
+        return s;
+    }
+
+    /**
+     * Returns a new set of first 5 ints.
+     */
+    private NavigableSet set5() {
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        assertTrue(q.isEmpty());
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        q.add(four);
+        q.add(five);
+        q.add(zero);
+        q.add(seven);
+        NavigableSet s = q.subSet(one, true, seven, false);
+        assertEquals(5, s.size());
+        return s;
+    }
+
+    /**
+     * Returns a new set of first 5 negative ints.
+     */
+    private NavigableSet dset5() {
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        assertTrue(q.isEmpty());
+        q.add(m1);
+        q.add(m2);
+        q.add(m3);
+        q.add(m4);
+        q.add(m5);
+        NavigableSet s = q.descendingSet();
+        assertEquals(5, s.size());
+        return s;
+    }
+
+    private static NavigableSet set0() {
+        ConcurrentSkipListSet set = new ConcurrentSkipListSet();
+        assertTrue(set.isEmpty());
+        return set.tailSet(m1, true);
+    }
+
+    private static NavigableSet dset0() {
+        ConcurrentSkipListSet set = new ConcurrentSkipListSet();
+        assertTrue(set.isEmpty());
+        return set;
+    }
+
+    /**
+     * A new set has unbounded capacity
+     */
+    public void testConstructor1() {
+        assertEquals(0, set0().size());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        NavigableSet q = set0();
+        assertTrue(q.isEmpty());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.add(new Integer(2));
+        q.pollFirst();
+        q.pollFirst();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testSize() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i, q.size());
+            q.pollFirst();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * add(null) throws NPE
+     */
+    public void testAddNull() {
+        NavigableSet q = set0();
+        try {
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Add of comparable element succeeds
+     */
+    public void testAdd() {
+        NavigableSet q = set0();
+        assertTrue(q.add(six));
+    }
+
+    /**
+     * Add of duplicate element fails
+     */
+    public void testAddDup() {
+        NavigableSet q = set0();
+        assertTrue(q.add(six));
+        assertFalse(q.add(six));
+    }
+
+    /**
+     * Add of non-Comparable throws CCE
+     */
+    public void testAddNonComparable() {
+        NavigableSet q = set0();
+        try {
+            q.add(new Object());
+            q.add(new Object());
+            shouldThrow();
+        } catch (ClassCastException success) {}
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        NavigableSet q = set0();
+        try {
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testAddAll2() {
+        NavigableSet q = set0();
+        Integer[] ints = new Integer[SIZE];
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        NavigableSet q = set0();
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i + SIZE);
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Set contains all elements of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(SIZE - 1 - i);
+        NavigableSet q = set0();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(new Integer(i), q.pollFirst());
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testRemoveElement() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertTrue(q.contains(i - 1));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertFalse(q.remove(i + 1));
+            assertFalse(q.contains(i + 1));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.pollFirst();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        NavigableSet q = populatedSet(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        NavigableSet q = populatedSet(SIZE);
+        NavigableSet p = set0();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        NavigableSet q = populatedSet(SIZE);
+        NavigableSet p = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.pollFirst();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            NavigableSet q = populatedSet(SIZE);
+            NavigableSet p = populatedSet(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer x = (Integer)(p.pollFirst());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    /**
+     * lower returns preceding element
+     */
+    public void testLower() {
+        NavigableSet q = set5();
+        Object e1 = q.lower(three);
+        assertEquals(two, e1);
+
+        Object e2 = q.lower(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.lower(one);
+        assertNull(e3);
+
+        Object e4 = q.lower(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higher returns next element
+     */
+    public void testHigher() {
+        NavigableSet q = set5();
+        Object e1 = q.higher(three);
+        assertEquals(four, e1);
+
+        Object e2 = q.higher(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.higher(five);
+        assertNull(e3);
+
+        Object e4 = q.higher(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floor returns preceding element
+     */
+    public void testFloor() {
+        NavigableSet q = set5();
+        Object e1 = q.floor(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.floor(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.floor(one);
+        assertEquals(one, e3);
+
+        Object e4 = q.floor(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceiling returns next element
+     */
+    public void testCeiling() {
+        NavigableSet q = set5();
+        Object e1 = q.ceiling(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.ceiling(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.ceiling(five);
+        assertEquals(five, e3);
+
+        Object e4 = q.ceiling(six);
+        assertNull(e4);
+    }
+
+    /**
+     * toArray contains all elements in sorted order
+     */
+    public void testToArray() {
+        NavigableSet q = populatedSet(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.pollFirst());
+    }
+
+    /**
+     * toArray(a) contains all elements in sorted order
+     */
+    public void testToArray2() {
+        NavigableSet<Integer> q = populatedSet(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.pollFirst());
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() {
+        NavigableSet q = populatedSet(SIZE);
+        Iterator it = q.iterator();
+        int i;
+        for (i = 0; it.hasNext(); i++)
+            assertTrue(q.contains(it.next()));
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty set has no elements
+     */
+    public void testEmptyIterator() {
+        assertIteratorExhausted(set0().iterator());
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final NavigableSet q = set0();
+        q.add(new Integer(2));
+        q.add(new Integer(1));
+        q.add(new Integer(3));
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertEquals(it.next(), new Integer(2));
+        assertEquals(it.next(), new Integer(3));
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        NavigableSet q = populatedSet(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized set has same elements
+     */
+    public void testSerialization() throws Exception {
+        NavigableSet x = populatedSet(SIZE);
+        NavigableSet y = serialClone(x);
+
+        assertNotSame(y, x);
+        assertEquals(x.size(), y.size());
+        assertEquals(x, y);
+        assertEquals(y, x);
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.pollFirst(), y.pollFirst());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * subSet returns set with keys in requested range
+     */
+    public void testSubSetContents() {
+        NavigableSet set = set5();
+        SortedSet sm = set.subSet(two, four);
+        assertEquals(two, sm.first());
+        assertEquals(three, sm.last());
+        assertEquals(2, sm.size());
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(two));
+        assertEquals(4, set.size());
+        assertEquals(1, sm.size());
+        assertEquals(three, sm.first());
+        assertEquals(three, sm.last());
+        assertTrue(sm.remove(three));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, set.size());
+    }
+
+    public void testSubSetContents2() {
+        NavigableSet set = set5();
+        SortedSet sm = set.subSet(two, three);
+        assertEquals(1, sm.size());
+        assertEquals(two, sm.first());
+        assertEquals(two, sm.last());
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertFalse(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(two));
+        assertEquals(4, set.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertFalse(sm.remove(three));
+        assertEquals(4, set.size());
+    }
+
+    /**
+     * headSet returns set with keys in requested range
+     */
+    public void testHeadSetContents() {
+        NavigableSet set = set5();
+        SortedSet sm = set.headSet(four);
+        assertTrue(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(one, k);
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, set.size());
+        assertEquals(four, set.first());
+    }
+
+    /**
+     * tailSet returns set with keys in requested range
+     */
+    public void testTailSetContents() {
+        NavigableSet set = set5();
+        SortedSet sm = set.tailSet(two);
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertTrue(sm.contains(four));
+        assertTrue(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        k = (Integer)(i.next());
+        assertEquals(four, k);
+        k = (Integer)(i.next());
+        assertEquals(five, k);
+        assertFalse(i.hasNext());
+
+        SortedSet ssm = sm.tailSet(four);
+        assertEquals(four, ssm.first());
+        assertEquals(five, ssm.last());
+        assertTrue(ssm.remove(four));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, set.size());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testDescendingSize() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i, q.size());
+            q.pollFirst();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * add(null) throws NPE
+     */
+    public void testDescendingAddNull() {
+        NavigableSet q = dset0();
+        try {
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Add of comparable element succeeds
+     */
+    public void testDescendingAdd() {
+        NavigableSet q = dset0();
+        assertTrue(q.add(m6));
+    }
+
+    /**
+     * Add of duplicate element fails
+     */
+    public void testDescendingAddDup() {
+        NavigableSet q = dset0();
+        assertTrue(q.add(m6));
+        assertFalse(q.add(m6));
+    }
+
+    /**
+     * Add of non-Comparable throws CCE
+     */
+    public void testDescendingAddNonComparable() {
+        NavigableSet q = dset0();
+        try {
+            q.add(new Object());
+            q.add(new Object());
+            shouldThrow();
+        } catch (ClassCastException success) {}
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testDescendingAddAll1() {
+        NavigableSet q = dset0();
+        try {
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testDescendingAddAll2() {
+        NavigableSet q = dset0();
+        Integer[] ints = new Integer[SIZE];
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testDescendingAddAll3() {
+        NavigableSet q = dset0();
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i + SIZE);
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Set contains all elements of successful addAll
+     */
+    public void testDescendingAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(SIZE - 1 - i);
+        NavigableSet q = dset0();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(new Integer(i), q.pollFirst());
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testDescendingPoll() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testDescendingRemoveElement() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.remove(new Integer(i)));
+        }
+        for (int i = 0; i < SIZE; i += 2 ) {
+            assertTrue(q.remove(new Integer(i)));
+            assertFalse(q.remove(new Integer(i + 1)));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testDescendingContains() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.pollFirst();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testDescendingClear() {
+        NavigableSet q = populatedSet(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testDescendingContainsAll() {
+        NavigableSet q = populatedSet(SIZE);
+        NavigableSet p = dset0();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testDescendingRetainAll() {
+        NavigableSet q = populatedSet(SIZE);
+        NavigableSet p = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.pollFirst();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testDescendingRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            NavigableSet q = populatedSet(SIZE);
+            NavigableSet p = populatedSet(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer x = (Integer)(p.pollFirst());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    /**
+     * lower returns preceding element
+     */
+    public void testDescendingLower() {
+        NavigableSet q = dset5();
+        Object e1 = q.lower(m3);
+        assertEquals(m2, e1);
+
+        Object e2 = q.lower(m6);
+        assertEquals(m5, e2);
+
+        Object e3 = q.lower(m1);
+        assertNull(e3);
+
+        Object e4 = q.lower(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higher returns next element
+     */
+    public void testDescendingHigher() {
+        NavigableSet q = dset5();
+        Object e1 = q.higher(m3);
+        assertEquals(m4, e1);
+
+        Object e2 = q.higher(zero);
+        assertEquals(m1, e2);
+
+        Object e3 = q.higher(m5);
+        assertNull(e3);
+
+        Object e4 = q.higher(m6);
+        assertNull(e4);
+    }
+
+    /**
+     * floor returns preceding element
+     */
+    public void testDescendingFloor() {
+        NavigableSet q = dset5();
+        Object e1 = q.floor(m3);
+        assertEquals(m3, e1);
+
+        Object e2 = q.floor(m6);
+        assertEquals(m5, e2);
+
+        Object e3 = q.floor(m1);
+        assertEquals(m1, e3);
+
+        Object e4 = q.floor(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceiling returns next element
+     */
+    public void testDescendingCeiling() {
+        NavigableSet q = dset5();
+        Object e1 = q.ceiling(m3);
+        assertEquals(m3, e1);
+
+        Object e2 = q.ceiling(zero);
+        assertEquals(m1, e2);
+
+        Object e3 = q.ceiling(m5);
+        assertEquals(m5, e3);
+
+        Object e4 = q.ceiling(m6);
+        assertNull(e4);
+    }
+
+    /**
+     * toArray contains all elements
+     */
+    public void testDescendingToArray() {
+        NavigableSet q = populatedSet(SIZE);
+        Object[] o = q.toArray();
+        Arrays.sort(o);
+        for (int i = 0; i < o.length; i++)
+            assertEquals(o[i], q.pollFirst());
+    }
+
+    /**
+     * toArray(a) contains all elements
+     */
+    public void testDescendingToArray2() {
+        NavigableSet q = populatedSet(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        assertSame(ints, q.toArray(ints));
+        Arrays.sort(ints);
+        for (int i = 0; i < ints.length; i++)
+            assertEquals(ints[i], q.pollFirst());
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testDescendingIterator() {
+        NavigableSet q = populatedSet(SIZE);
+        int i = 0;
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+    }
+
+    /**
+     * iterator of empty set has no elements
+     */
+    public void testDescendingEmptyIterator() {
+        NavigableSet q = dset0();
+        int i = 0;
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(0, i);
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testDescendingIteratorRemove() {
+        final NavigableSet q = dset0();
+        q.add(new Integer(2));
+        q.add(new Integer(1));
+        q.add(new Integer(3));
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertEquals(it.next(), new Integer(2));
+        assertEquals(it.next(), new Integer(3));
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testDescendingToString() {
+        NavigableSet q = populatedSet(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized set has same elements
+     */
+    public void testDescendingSerialization() throws Exception {
+        NavigableSet x = dset5();
+        NavigableSet y = serialClone(x);
+
+        assertNotSame(y, x);
+        assertEquals(x.size(), y.size());
+        assertEquals(x, y);
+        assertEquals(y, x);
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.pollFirst(), y.pollFirst());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * subSet returns set with keys in requested range
+     */
+    public void testDescendingSubSetContents() {
+        NavigableSet set = dset5();
+        SortedSet sm = set.subSet(m2, m4);
+        assertEquals(m2, sm.first());
+        assertEquals(m3, sm.last());
+        assertEquals(2, sm.size());
+        assertFalse(sm.contains(m1));
+        assertTrue(sm.contains(m2));
+        assertTrue(sm.contains(m3));
+        assertFalse(sm.contains(m4));
+        assertFalse(sm.contains(m5));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(m2));
+        assertEquals(4, set.size());
+        assertEquals(1, sm.size());
+        assertEquals(m3, sm.first());
+        assertEquals(m3, sm.last());
+        assertTrue(sm.remove(m3));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, set.size());
+    }
+
+    public void testDescendingSubSetContents2() {
+        NavigableSet set = dset5();
+        SortedSet sm = set.subSet(m2, m3);
+        assertEquals(1, sm.size());
+        assertEquals(m2, sm.first());
+        assertEquals(m2, sm.last());
+        assertFalse(sm.contains(m1));
+        assertTrue(sm.contains(m2));
+        assertFalse(sm.contains(m3));
+        assertFalse(sm.contains(m4));
+        assertFalse(sm.contains(m5));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(m2));
+        assertEquals(4, set.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertFalse(sm.remove(m3));
+        assertEquals(4, set.size());
+    }
+
+    /**
+     * headSet returns set with keys in requested range
+     */
+    public void testDescendingHeadSetContents() {
+        NavigableSet set = dset5();
+        SortedSet sm = set.headSet(m4);
+        assertTrue(sm.contains(m1));
+        assertTrue(sm.contains(m2));
+        assertTrue(sm.contains(m3));
+        assertFalse(sm.contains(m4));
+        assertFalse(sm.contains(m5));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m1, k);
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, set.size());
+        assertEquals(m4, set.first());
+    }
+
+    /**
+     * tailSet returns set with keys in requested range
+     */
+    public void testDescendingTailSetContents() {
+        NavigableSet set = dset5();
+        SortedSet sm = set.tailSet(m2);
+        assertFalse(sm.contains(m1));
+        assertTrue(sm.contains(m2));
+        assertTrue(sm.contains(m3));
+        assertTrue(sm.contains(m4));
+        assertTrue(sm.contains(m5));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        k = (Integer)(i.next());
+        assertEquals(m4, k);
+        k = (Integer)(i.next());
+        assertEquals(m5, k);
+        assertFalse(i.hasNext());
+
+        SortedSet ssm = sm.tailSet(m4);
+        assertEquals(m4, ssm.first());
+        assertEquals(m5, ssm.last());
+        assertTrue(ssm.remove(m4));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, set.size());
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/CopyOnWriteArrayListTest.java b/jdk/test/java/util/concurrent/tck/CopyOnWriteArrayListTest.java
new file mode 100644
index 00000000000..75cfb1435fc
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/CopyOnWriteArrayListTest.java
@@ -0,0 +1,783 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.NoSuchElementException;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class CopyOnWriteArrayListTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(CopyOnWriteArrayListTest.class);
+    }
+
+    static CopyOnWriteArrayList<Integer> populatedArray(int n) {
+        CopyOnWriteArrayList<Integer> a = new CopyOnWriteArrayList<Integer>();
+        assertTrue(a.isEmpty());
+        for (int i = 0; i < n; i++)
+            a.add(i);
+        assertFalse(a.isEmpty());
+        assertEquals(n, a.size());
+        return a;
+    }
+
+    static CopyOnWriteArrayList<Integer> populatedArray(Integer[] elements) {
+        CopyOnWriteArrayList<Integer> a = new CopyOnWriteArrayList<Integer>();
+        assertTrue(a.isEmpty());
+        for (int i = 0; i < elements.length; i++)
+            a.add(elements[i]);
+        assertFalse(a.isEmpty());
+        assertEquals(elements.length, a.size());
+        return a;
+    }
+
+    /**
+     * a new list is empty
+     */
+    public void testConstructor() {
+        CopyOnWriteArrayList a = new CopyOnWriteArrayList();
+        assertTrue(a.isEmpty());
+    }
+
+    /**
+     * new list contains all elements of initializing array
+     */
+    public void testConstructor2() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        CopyOnWriteArrayList a = new CopyOnWriteArrayList(ints);
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], a.get(i));
+    }
+
+    /**
+     * new list contains all elements of initializing collection
+     */
+    public void testConstructor3() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        CopyOnWriteArrayList a = new CopyOnWriteArrayList(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], a.get(i));
+    }
+
+    /**
+     * addAll adds each element from the given collection, including duplicates
+     */
+    public void testAddAll() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        assertTrue(full.addAll(Arrays.asList(three, four, five)));
+        assertEquals(6, full.size());
+        assertTrue(full.addAll(Arrays.asList(three, four, five)));
+        assertEquals(9, full.size());
+    }
+
+    /**
+     * addAllAbsent adds each element from the given collection that did not
+     * already exist in the List
+     */
+    public void testAddAllAbsent() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        // "one" is duplicate and will not be added
+        assertEquals(2, full.addAllAbsent(Arrays.asList(three, four, one)));
+        assertEquals(5, full.size());
+        assertEquals(0, full.addAllAbsent(Arrays.asList(three, four, one)));
+        assertEquals(5, full.size());
+    }
+
+    /**
+     * addIfAbsent will not add the element if it already exists in the list
+     */
+    public void testAddIfAbsent() {
+        CopyOnWriteArrayList full = populatedArray(SIZE);
+        full.addIfAbsent(one);
+        assertEquals(SIZE, full.size());
+    }
+
+    /**
+     * addIfAbsent adds the element when it does not exist in the list
+     */
+    public void testAddIfAbsent2() {
+        CopyOnWriteArrayList full = populatedArray(SIZE);
+        full.addIfAbsent(three);
+        assertTrue(full.contains(three));
+    }
+
+    /**
+     * clear removes all elements from the list
+     */
+    public void testClear() {
+        CopyOnWriteArrayList full = populatedArray(SIZE);
+        full.clear();
+        assertEquals(0, full.size());
+    }
+
+    /**
+     * Cloned list is equal
+     */
+    public void testClone() {
+        CopyOnWriteArrayList l1 = populatedArray(SIZE);
+        CopyOnWriteArrayList l2 = (CopyOnWriteArrayList)(l1.clone());
+        assertEquals(l1, l2);
+        l1.clear();
+        assertFalse(l1.equals(l2));
+    }
+
+    /**
+     * contains is true for added elements
+     */
+    public void testContains() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        assertTrue(full.contains(one));
+        assertFalse(full.contains(five));
+    }
+
+    /**
+     * adding at an index places it in the indicated index
+     */
+    public void testAddIndex() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        full.add(0, m1);
+        assertEquals(4, full.size());
+        assertEquals(m1, full.get(0));
+        assertEquals(zero, full.get(1));
+
+        full.add(2, m2);
+        assertEquals(5, full.size());
+        assertEquals(m2, full.get(2));
+        assertEquals(two, full.get(4));
+    }
+
+    /**
+     * lists with same elements are equal and have same hashCode
+     */
+    public void testEquals() {
+        CopyOnWriteArrayList a = populatedArray(3);
+        CopyOnWriteArrayList b = populatedArray(3);
+        assertTrue(a.equals(b));
+        assertTrue(b.equals(a));
+        assertTrue(a.containsAll(b));
+        assertTrue(b.containsAll(a));
+        assertEquals(a.hashCode(), b.hashCode());
+        a.add(m1);
+        assertFalse(a.equals(b));
+        assertFalse(b.equals(a));
+        assertTrue(a.containsAll(b));
+        assertFalse(b.containsAll(a));
+        b.add(m1);
+        assertTrue(a.equals(b));
+        assertTrue(b.equals(a));
+        assertTrue(a.containsAll(b));
+        assertTrue(b.containsAll(a));
+        assertEquals(a.hashCode(), b.hashCode());
+
+        assertFalse(a.equals(null));
+    }
+
+    /**
+     * containsAll returns true for collections with subset of elements
+     */
+    public void testContainsAll() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        assertTrue(full.containsAll(Arrays.asList()));
+        assertTrue(full.containsAll(Arrays.asList(one)));
+        assertTrue(full.containsAll(Arrays.asList(one, two)));
+        assertFalse(full.containsAll(Arrays.asList(one, two, six)));
+        assertFalse(full.containsAll(Arrays.asList(six)));
+
+        try {
+            full.containsAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * get returns the value at the given index
+     */
+    public void testGet() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        assertEquals(0, full.get(0));
+    }
+
+    /**
+     * indexOf gives the index for the given object
+     */
+    public void testIndexOf() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        assertEquals(1, full.indexOf(one));
+        assertEquals(-1, full.indexOf("puppies"));
+    }
+
+    /**
+     * indexOf gives the index based on the given index
+     * at which to start searching
+     */
+    public void testIndexOf2() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        assertEquals(1, full.indexOf(one, 0));
+        assertEquals(-1, full.indexOf(one, 2));
+    }
+
+    /**
+     * isEmpty returns true when empty, else false
+     */
+    public void testIsEmpty() {
+        CopyOnWriteArrayList empty = new CopyOnWriteArrayList();
+        CopyOnWriteArrayList full = populatedArray(SIZE);
+        assertTrue(empty.isEmpty());
+        assertFalse(full.isEmpty());
+    }
+
+    /**
+     * iterator() returns an iterator containing the elements of the
+     * list in insertion order
+     */
+    public void testIterator() {
+        Collection empty = new CopyOnWriteArrayList();
+        assertFalse(empty.iterator().hasNext());
+        try {
+            empty.iterator().next();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+
+        Integer[] elements = new Integer[SIZE];
+        for (int i = 0; i < SIZE; i++)
+            elements[i] = i;
+        Collections.shuffle(Arrays.asList(elements));
+        Collection<Integer> full = populatedArray(elements);
+
+        Iterator it = full.iterator();
+        for (int j = 0; j < SIZE; j++) {
+            assertTrue(it.hasNext());
+            assertEquals(elements[j], it.next());
+        }
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty collection has no elements
+     */
+    public void testEmptyIterator() {
+        Collection c = new CopyOnWriteArrayList();
+        assertIteratorExhausted(c.iterator());
+    }
+
+    /**
+     * iterator.remove throws UnsupportedOperationException
+     */
+    public void testIteratorRemove() {
+        CopyOnWriteArrayList full = populatedArray(SIZE);
+        Iterator it = full.iterator();
+        it.next();
+        try {
+            it.remove();
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+    }
+
+    /**
+     * toString contains toString of elements
+     */
+    public void testToString() {
+        assertEquals("[]", new CopyOnWriteArrayList().toString());
+        CopyOnWriteArrayList full = populatedArray(3);
+        String s = full.toString();
+        for (int i = 0; i < 3; ++i)
+            assertTrue(s.contains(String.valueOf(i)));
+        assertEquals(new ArrayList(full).toString(),
+                     full.toString());
+    }
+
+    /**
+     * lastIndexOf returns the index for the given object
+     */
+    public void testLastIndexOf1() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        full.add(one);
+        full.add(three);
+        assertEquals(3, full.lastIndexOf(one));
+        assertEquals(-1, full.lastIndexOf(six));
+    }
+
+    /**
+     * lastIndexOf returns the index from the given starting point
+     */
+    public void testLastIndexOf2() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        full.add(one);
+        full.add(three);
+        assertEquals(3, full.lastIndexOf(one, 4));
+        assertEquals(-1, full.lastIndexOf(three, 3));
+    }
+
+    /**
+     * listIterator traverses all elements
+     */
+    public void testListIterator1() {
+        CopyOnWriteArrayList full = populatedArray(SIZE);
+        ListIterator i = full.listIterator();
+        int j;
+        for (j = 0; i.hasNext(); j++)
+            assertEquals(j, i.next());
+        assertEquals(SIZE, j);
+    }
+
+    /**
+     * listIterator only returns those elements after the given index
+     */
+    public void testListIterator2() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        ListIterator i = full.listIterator(1);
+        int j;
+        for (j = 0; i.hasNext(); j++)
+            assertEquals(j + 1, i.next());
+        assertEquals(2, j);
+    }
+
+    /**
+     * remove(int) removes and returns the object at the given index
+     */
+    public void testRemove_int() {
+        int SIZE = 3;
+        for (int i = 0; i < SIZE; i++) {
+            CopyOnWriteArrayList full = populatedArray(SIZE);
+            assertEquals(i, full.remove(i));
+            assertEquals(SIZE - 1, full.size());
+            assertFalse(full.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * remove(Object) removes the object if found and returns true
+     */
+    public void testRemove_Object() {
+        int SIZE = 3;
+        for (int i = 0; i < SIZE; i++) {
+            CopyOnWriteArrayList full = populatedArray(SIZE);
+            assertFalse(full.remove(new Integer(-42)));
+            assertTrue(full.remove(new Integer(i)));
+            assertEquals(SIZE - 1, full.size());
+            assertFalse(full.contains(new Integer(i)));
+        }
+        CopyOnWriteArrayList x = new CopyOnWriteArrayList(Arrays.asList(4, 5, 6));
+        assertTrue(x.remove(new Integer(6)));
+        assertEquals(x, Arrays.asList(4, 5));
+        assertTrue(x.remove(new Integer(4)));
+        assertEquals(x, Arrays.asList(5));
+        assertTrue(x.remove(new Integer(5)));
+        assertEquals(x, Arrays.asList());
+        assertFalse(x.remove(new Integer(5)));
+    }
+
+    /**
+     * removeAll removes all elements from the given collection
+     */
+    public void testRemoveAll() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        assertTrue(full.removeAll(Arrays.asList(one, two)));
+        assertEquals(1, full.size());
+        assertFalse(full.removeAll(Arrays.asList(one, two)));
+        assertEquals(1, full.size());
+    }
+
+    /**
+     * set changes the element at the given index
+     */
+    public void testSet() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        assertEquals(2, full.set(2, four));
+        assertEquals(4, full.get(2));
+    }
+
+    /**
+     * size returns the number of elements
+     */
+    public void testSize() {
+        CopyOnWriteArrayList empty = new CopyOnWriteArrayList();
+        CopyOnWriteArrayList full = populatedArray(SIZE);
+        assertEquals(SIZE, full.size());
+        assertEquals(0, empty.size());
+    }
+
+    /**
+     * toArray() returns an Object array containing all elements from
+     * the list in insertion order
+     */
+    public void testToArray() {
+        Object[] a = new CopyOnWriteArrayList().toArray();
+        assertTrue(Arrays.equals(new Object[0], a));
+        assertSame(Object[].class, a.getClass());
+
+        Integer[] elements = new Integer[SIZE];
+        for (int i = 0; i < SIZE; i++)
+            elements[i] = i;
+        Collections.shuffle(Arrays.asList(elements));
+        Collection<Integer> full = populatedArray(elements);
+
+        assertTrue(Arrays.equals(elements, full.toArray()));
+        assertSame(Object[].class, full.toArray().getClass());
+    }
+
+    /**
+     * toArray(Integer array) returns an Integer array containing all
+     * elements from the list in insertion order
+     */
+    public void testToArray2() {
+        Collection empty = new CopyOnWriteArrayList();
+        Integer[] a;
+
+        a = new Integer[0];
+        assertSame(a, empty.toArray(a));
+
+        a = new Integer[SIZE / 2];
+        Arrays.fill(a, 42);
+        assertSame(a, empty.toArray(a));
+        assertNull(a[0]);
+        for (int i = 1; i < a.length; i++)
+            assertEquals(42, (int) a[i]);
+
+        Integer[] elements = new Integer[SIZE];
+        for (int i = 0; i < SIZE; i++)
+            elements[i] = i;
+        Collections.shuffle(Arrays.asList(elements));
+        Collection<Integer> full = populatedArray(elements);
+
+        Arrays.fill(a, 42);
+        assertTrue(Arrays.equals(elements, full.toArray(a)));
+        for (int i = 0; i < a.length; i++)
+            assertEquals(42, (int) a[i]);
+        assertSame(Integer[].class, full.toArray(a).getClass());
+
+        a = new Integer[SIZE];
+        Arrays.fill(a, 42);
+        assertSame(a, full.toArray(a));
+        assertTrue(Arrays.equals(elements, a));
+
+        a = new Integer[2 * SIZE];
+        Arrays.fill(a, 42);
+        assertSame(a, full.toArray(a));
+        assertTrue(Arrays.equals(elements, Arrays.copyOf(a, SIZE)));
+        assertNull(a[SIZE]);
+        for (int i = SIZE + 1; i < a.length; i++)
+            assertEquals(42, (int) a[i]);
+    }
+
+    /**
+     * sublists contains elements at indexes offset from their base
+     */
+    public void testSubList() {
+        CopyOnWriteArrayList a = populatedArray(10);
+        assertTrue(a.subList(1,1).isEmpty());
+        for (int j = 0; j < 9; ++j) {
+            for (int i = j ; i < 10; ++i) {
+                List b = a.subList(j,i);
+                for (int k = j; k < i; ++k) {
+                    assertEquals(new Integer(k), b.get(k-j));
+                }
+            }
+        }
+
+        List s = a.subList(2, 5);
+        assertEquals(3, s.size());
+        s.set(2, m1);
+        assertEquals(a.get(4), m1);
+        s.clear();
+        assertEquals(7, a.size());
+    }
+
+    // Exception tests
+
+    /**
+     * toArray throws an ArrayStoreException when the given array
+     * can not store the objects inside the list
+     */
+    public void testToArray_ArrayStoreException() {
+        CopyOnWriteArrayList c = new CopyOnWriteArrayList();
+        c.add("zfasdfsdf");
+        c.add("asdadasd");
+        try {
+            c.toArray(new Long[5]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * get throws an IndexOutOfBoundsException on a negative index
+     */
+    public void testGet1_IndexOutOfBoundsException() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.get(-1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * get throws an IndexOutOfBoundsException on a too high index
+     */
+    public void testGet2_IndexOutOfBoundsException() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.get(list.size());
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * set throws an IndexOutOfBoundsException on a negative index
+     */
+    public void testSet1_IndexOutOfBoundsException() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.set(-1, "qwerty");
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * set throws an IndexOutOfBoundsException on a too high index
+     */
+    public void testSet2() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.set(list.size(), "qwerty");
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * add throws an IndexOutOfBoundsException on a negative index
+     */
+    public void testAdd1_IndexOutOfBoundsException() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.add(-1, "qwerty");
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * add throws an IndexOutOfBoundsException on a too high index
+     */
+    public void testAdd2_IndexOutOfBoundsException() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.add(list.size() + 1, "qwerty");
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * remove throws an IndexOutOfBoundsException on a negative index
+     */
+    public void testRemove1_IndexOutOfBounds() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.remove(-1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * remove throws an IndexOutOfBoundsException on a too high index
+     */
+    public void testRemove2_IndexOutOfBounds() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.remove(list.size());
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * addAll throws an IndexOutOfBoundsException on a negative index
+     */
+    public void testAddAll1_IndexOutOfBoundsException() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.addAll(-1, new LinkedList());
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * addAll throws an IndexOutOfBoundsException on a too high index
+     */
+    public void testAddAll2_IndexOutOfBoundsException() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.addAll(list.size() + 1, new LinkedList());
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * listIterator throws an IndexOutOfBoundsException on a negative index
+     */
+    public void testListIterator1_IndexOutOfBoundsException() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.listIterator(-1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * listIterator throws an IndexOutOfBoundsException on a too high index
+     */
+    public void testListIterator2_IndexOutOfBoundsException() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.listIterator(list.size() + 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * subList throws an IndexOutOfBoundsException on a negative index
+     */
+    public void testSubList1_IndexOutOfBoundsException() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.subList(-1, list.size());
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * subList throws an IndexOutOfBoundsException on a too high index
+     */
+    public void testSubList2_IndexOutOfBoundsException() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.subList(0, list.size() + 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * subList throws IndexOutOfBoundsException when the second index
+     * is lower then the first
+     */
+    public void testSubList3_IndexOutOfBoundsException() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.subList(list.size() - 1, 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * a deserialized serialized list is equal
+     */
+    public void testSerialization() throws Exception {
+        List x = populatedArray(SIZE);
+        List y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertTrue(Arrays.equals(x.toArray(), y.toArray()));
+        assertEquals(x, y);
+        assertEquals(y, x);
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(0), y.remove(0));
+        }
+        assertTrue(y.isEmpty());
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/CopyOnWriteArraySetTest.java b/jdk/test/java/util/concurrent/tck/CopyOnWriteArraySetTest.java
new file mode 100644
index 00000000000..bd9a7e3da43
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/CopyOnWriteArraySetTest.java
@@ -0,0 +1,432 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class CopyOnWriteArraySetTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(CopyOnWriteArraySetTest.class);
+    }
+
+    static CopyOnWriteArraySet<Integer> populatedSet(int n) {
+        CopyOnWriteArraySet<Integer> a = new CopyOnWriteArraySet<Integer>();
+        assertTrue(a.isEmpty());
+        for (int i = 0; i < n; i++)
+            a.add(i);
+        assertEquals(n == 0, a.isEmpty());
+        assertEquals(n, a.size());
+        return a;
+    }
+
+    static CopyOnWriteArraySet populatedSet(Integer[] elements) {
+        CopyOnWriteArraySet<Integer> a = new CopyOnWriteArraySet<Integer>();
+        assertTrue(a.isEmpty());
+        for (int i = 0; i < elements.length; i++)
+            a.add(elements[i]);
+        assertFalse(a.isEmpty());
+        assertEquals(elements.length, a.size());
+        return a;
+    }
+
+    /**
+     * Default-constructed set is empty
+     */
+    public void testConstructor() {
+        CopyOnWriteArraySet a = new CopyOnWriteArraySet();
+        assertTrue(a.isEmpty());
+    }
+
+    /**
+     * Collection-constructed set holds all of its elements
+     */
+    public void testConstructor3() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        CopyOnWriteArraySet a = new CopyOnWriteArraySet(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertTrue(a.contains(ints[i]));
+    }
+
+    /**
+     * addAll adds each non-duplicate element from the given collection
+     */
+    public void testAddAll() {
+        Set full = populatedSet(3);
+        assertTrue(full.addAll(Arrays.asList(three, four, five)));
+        assertEquals(6, full.size());
+        assertFalse(full.addAll(Arrays.asList(three, four, five)));
+        assertEquals(6, full.size());
+    }
+
+    /**
+     * addAll adds each non-duplicate element from the given collection
+     */
+    public void testAddAll2() {
+        Set full = populatedSet(3);
+        // "one" is duplicate and will not be added
+        assertTrue(full.addAll(Arrays.asList(three, four, one)));
+        assertEquals(5, full.size());
+        assertFalse(full.addAll(Arrays.asList(three, four, one)));
+        assertEquals(5, full.size());
+    }
+
+    /**
+     * add will not add the element if it already exists in the set
+     */
+    public void testAdd2() {
+        Set full = populatedSet(3);
+        full.add(one);
+        assertEquals(3, full.size());
+    }
+
+    /**
+     * add adds the element when it does not exist in the set
+     */
+    public void testAdd3() {
+        Set full = populatedSet(3);
+        full.add(three);
+        assertTrue(full.contains(three));
+    }
+
+    /**
+     * clear removes all elements from the set
+     */
+    public void testClear() {
+        Collection full = populatedSet(3);
+        full.clear();
+        assertEquals(0, full.size());
+        assertTrue(full.isEmpty());
+    }
+
+    /**
+     * contains returns true for added elements
+     */
+    public void testContains() {
+        Collection full = populatedSet(3);
+        assertTrue(full.contains(one));
+        assertFalse(full.contains(five));
+    }
+
+    /**
+     * Sets with equal elements are equal
+     */
+    public void testEquals() {
+        CopyOnWriteArraySet a = populatedSet(3);
+        CopyOnWriteArraySet b = populatedSet(3);
+        assertTrue(a.equals(b));
+        assertTrue(b.equals(a));
+        assertTrue(a.containsAll(b));
+        assertTrue(b.containsAll(a));
+        assertEquals(a.hashCode(), b.hashCode());
+        assertEquals(a.size(), b.size());
+
+        a.add(m1);
+        assertFalse(a.equals(b));
+        assertFalse(b.equals(a));
+        assertTrue(a.containsAll(b));
+        assertFalse(b.containsAll(a));
+        b.add(m1);
+        assertTrue(a.equals(b));
+        assertTrue(b.equals(a));
+        assertTrue(a.containsAll(b));
+        assertTrue(b.containsAll(a));
+        assertEquals(a.hashCode(), b.hashCode());
+
+        Object x = a.iterator().next();
+        a.remove(x);
+        assertFalse(a.equals(b));
+        assertFalse(b.equals(a));
+        assertFalse(a.containsAll(b));
+        assertTrue(b.containsAll(a));
+        a.add(x);
+        assertTrue(a.equals(b));
+        assertTrue(b.equals(a));
+        assertTrue(a.containsAll(b));
+        assertTrue(b.containsAll(a));
+        assertEquals(a.hashCode(), b.hashCode());
+        assertEquals(a.size(), b.size());
+
+        CopyOnWriteArraySet empty1 = new CopyOnWriteArraySet(Arrays.asList());
+        CopyOnWriteArraySet empty2 = new CopyOnWriteArraySet(Arrays.asList());
+        assertTrue(empty1.equals(empty1));
+        assertTrue(empty1.equals(empty2));
+
+        assertFalse(empty1.equals(a));
+        assertFalse(a.equals(empty1));
+
+        assertFalse(a.equals(null));
+    }
+
+    /**
+     * containsAll returns true for collections with subset of elements
+     */
+    public void testContainsAll() {
+        Collection full = populatedSet(3);
+        assertTrue(full.containsAll(full));
+        assertTrue(full.containsAll(Arrays.asList()));
+        assertTrue(full.containsAll(Arrays.asList(one)));
+        assertTrue(full.containsAll(Arrays.asList(one, two)));
+        assertFalse(full.containsAll(Arrays.asList(one, two, six)));
+        assertFalse(full.containsAll(Arrays.asList(six)));
+
+        CopyOnWriteArraySet empty1 = new CopyOnWriteArraySet(Arrays.asList());
+        CopyOnWriteArraySet empty2 = new CopyOnWriteArraySet(Arrays.asList());
+        assertTrue(empty1.containsAll(empty2));
+        assertTrue(empty1.containsAll(empty1));
+        assertFalse(empty1.containsAll(full));
+        assertTrue(full.containsAll(empty1));
+
+        try {
+            full.containsAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * isEmpty is true when empty, else false
+     */
+    public void testIsEmpty() {
+        assertTrue(populatedSet(0).isEmpty());
+        assertFalse(populatedSet(3).isEmpty());
+    }
+
+    /**
+     * iterator() returns an iterator containing the elements of the
+     * set in insertion order
+     */
+    public void testIterator() {
+        Collection empty = new CopyOnWriteArraySet();
+        assertFalse(empty.iterator().hasNext());
+        try {
+            empty.iterator().next();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+
+        Integer[] elements = new Integer[SIZE];
+        for (int i = 0; i < SIZE; i++)
+            elements[i] = i;
+        Collections.shuffle(Arrays.asList(elements));
+        Collection<Integer> full = populatedSet(elements);
+
+        Iterator it = full.iterator();
+        for (int j = 0; j < SIZE; j++) {
+            assertTrue(it.hasNext());
+            assertEquals(elements[j], it.next());
+        }
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty collection has no elements
+     */
+    public void testEmptyIterator() {
+        assertIteratorExhausted(new CopyOnWriteArraySet().iterator());
+    }
+
+    /**
+     * iterator remove is unsupported
+     */
+    public void testIteratorRemove() {
+        Collection full = populatedSet(3);
+        Iterator it = full.iterator();
+        it.next();
+        try {
+            it.remove();
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+    }
+
+    /**
+     * toString holds toString of elements
+     */
+    public void testToString() {
+        assertEquals("[]", new CopyOnWriteArraySet().toString());
+        Collection full = populatedSet(3);
+        String s = full.toString();
+        for (int i = 0; i < 3; ++i)
+            assertTrue(s.contains(String.valueOf(i)));
+        assertEquals(new ArrayList(full).toString(),
+                     full.toString());
+    }
+
+    /**
+     * removeAll removes all elements from the given collection
+     */
+    public void testRemoveAll() {
+        Set full = populatedSet(3);
+        assertTrue(full.removeAll(Arrays.asList(one, two)));
+        assertEquals(1, full.size());
+        assertFalse(full.removeAll(Arrays.asList(one, two)));
+        assertEquals(1, full.size());
+    }
+
+    /**
+     * remove removes an element
+     */
+    public void testRemove() {
+        Collection full = populatedSet(3);
+        full.remove(one);
+        assertFalse(full.contains(one));
+        assertEquals(2, full.size());
+    }
+
+    /**
+     * size returns the number of elements
+     */
+    public void testSize() {
+        Collection empty = new CopyOnWriteArraySet();
+        Collection full = populatedSet(3);
+        assertEquals(3, full.size());
+        assertEquals(0, empty.size());
+    }
+
+    /**
+     * toArray() returns an Object array containing all elements from
+     * the set in insertion order
+     */
+    public void testToArray() {
+        Object[] a = new CopyOnWriteArraySet().toArray();
+        assertTrue(Arrays.equals(new Object[0], a));
+        assertSame(Object[].class, a.getClass());
+
+        Integer[] elements = new Integer[SIZE];
+        for (int i = 0; i < SIZE; i++)
+            elements[i] = i;
+        Collections.shuffle(Arrays.asList(elements));
+        Collection<Integer> full = populatedSet(elements);
+
+        assertTrue(Arrays.equals(elements, full.toArray()));
+        assertSame(Object[].class, full.toArray().getClass());
+    }
+
+    /**
+     * toArray(Integer array) returns an Integer array containing all
+     * elements from the set in insertion order
+     */
+    public void testToArray2() {
+        Collection empty = new CopyOnWriteArraySet();
+        Integer[] a;
+
+        a = new Integer[0];
+        assertSame(a, empty.toArray(a));
+
+        a = new Integer[SIZE / 2];
+        Arrays.fill(a, 42);
+        assertSame(a, empty.toArray(a));
+        assertNull(a[0]);
+        for (int i = 1; i < a.length; i++)
+            assertEquals(42, (int) a[i]);
+
+        Integer[] elements = new Integer[SIZE];
+        for (int i = 0; i < SIZE; i++)
+            elements[i] = i;
+        Collections.shuffle(Arrays.asList(elements));
+        Collection<Integer> full = populatedSet(elements);
+
+        Arrays.fill(a, 42);
+        assertTrue(Arrays.equals(elements, full.toArray(a)));
+        for (int i = 0; i < a.length; i++)
+            assertEquals(42, (int) a[i]);
+        assertSame(Integer[].class, full.toArray(a).getClass());
+
+        a = new Integer[SIZE];
+        Arrays.fill(a, 42);
+        assertSame(a, full.toArray(a));
+        assertTrue(Arrays.equals(elements, a));
+
+        a = new Integer[2 * SIZE];
+        Arrays.fill(a, 42);
+        assertSame(a, full.toArray(a));
+        assertTrue(Arrays.equals(elements, Arrays.copyOf(a, SIZE)));
+        assertNull(a[SIZE]);
+        for (int i = SIZE + 1; i < a.length; i++)
+            assertEquals(42, (int) a[i]);
+    }
+
+    /**
+     * toArray throws an ArrayStoreException when the given array can
+     * not store the objects inside the set
+     */
+    public void testToArray_ArrayStoreException() {
+        CopyOnWriteArraySet c = new CopyOnWriteArraySet();
+        c.add("zfasdfsdf");
+        c.add("asdadasd");
+        try {
+            c.toArray(new Long[5]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * A deserialized serialized set is equal
+     */
+    public void testSerialization() throws Exception {
+        Set x = populatedSet(SIZE);
+        Set y = serialClone(x);
+
+        assertNotSame(y, x);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertTrue(Arrays.equals(x.toArray(), y.toArray()));
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    /**
+     * addAll is idempotent
+     */
+    public void testAddAll_idempotent() throws Exception {
+        Set x = populatedSet(SIZE);
+        Set y = new CopyOnWriteArraySet(x);
+        y.addAll(x);
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/CountDownLatchTest.java b/jdk/test/java/util/concurrent/tck/CountDownLatchTest.java
new file mode 100644
index 00000000000..e286d00cdf9
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/CountDownLatchTest.java
@@ -0,0 +1,223 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.concurrent.CountDownLatch;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class CountDownLatchTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(CountDownLatchTest.class);
+    }
+
+    /**
+     * negative constructor argument throws IAE
+     */
+    public void testConstructor() {
+        try {
+            new CountDownLatch(-1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * getCount returns initial count and decreases after countDown
+     */
+    public void testGetCount() {
+        final CountDownLatch l = new CountDownLatch(2);
+        assertEquals(2, l.getCount());
+        l.countDown();
+        assertEquals(1, l.getCount());
+    }
+
+    /**
+     * countDown decrements count when positive and has no effect when zero
+     */
+    public void testCountDown() {
+        final CountDownLatch l = new CountDownLatch(1);
+        assertEquals(1, l.getCount());
+        l.countDown();
+        assertEquals(0, l.getCount());
+        l.countDown();
+        assertEquals(0, l.getCount());
+    }
+
+    /**
+     * await returns after countDown to zero, but not before
+     */
+    public void testAwait() {
+        final CountDownLatch l = new CountDownLatch(2);
+        final CountDownLatch pleaseCountDown = new CountDownLatch(1);
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertEquals(2, l.getCount());
+                pleaseCountDown.countDown();
+                l.await();
+                assertEquals(0, l.getCount());
+            }});
+
+        await(pleaseCountDown);
+        assertEquals(2, l.getCount());
+        l.countDown();
+        assertEquals(1, l.getCount());
+        assertThreadStaysAlive(t);
+        l.countDown();
+        assertEquals(0, l.getCount());
+        awaitTermination(t);
+    }
+
+    /**
+     * timed await returns after countDown to zero
+     */
+    public void testTimedAwait() {
+        final CountDownLatch l = new CountDownLatch(2);
+        final CountDownLatch pleaseCountDown = new CountDownLatch(1);
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertEquals(2, l.getCount());
+                pleaseCountDown.countDown();
+                assertTrue(l.await(LONG_DELAY_MS, MILLISECONDS));
+                assertEquals(0, l.getCount());
+            }});
+
+        await(pleaseCountDown);
+        assertEquals(2, l.getCount());
+        l.countDown();
+        assertEquals(1, l.getCount());
+        assertThreadStaysAlive(t);
+        l.countDown();
+        assertEquals(0, l.getCount());
+        awaitTermination(t);
+    }
+
+    /**
+     * await throws IE if interrupted before counted down
+     */
+    public void testAwait_Interruptible() {
+        final CountDownLatch l = new CountDownLatch(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                Thread.currentThread().interrupt();
+                try {
+                    l.await();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    l.await();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                assertEquals(1, l.getCount());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * timed await throws IE if interrupted before counted down
+     */
+    public void testTimedAwait_Interruptible() {
+        final CountDownLatch l = new CountDownLatch(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                Thread.currentThread().interrupt();
+                try {
+                    l.await(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    l.await(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                assertEquals(1, l.getCount());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * timed await times out if not counted down before timeout
+     */
+    public void testAwaitTimeout() throws InterruptedException {
+        final CountDownLatch l = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertEquals(1, l.getCount());
+                assertFalse(l.await(timeoutMillis(), MILLISECONDS));
+                assertEquals(1, l.getCount());
+            }});
+
+        awaitTermination(t);
+        assertEquals(1, l.getCount());
+    }
+
+    /**
+     * toString indicates current count
+     */
+    public void testToString() {
+        CountDownLatch s = new CountDownLatch(2);
+        assertTrue(s.toString().contains("Count = 2"));
+        s.countDown();
+        assertTrue(s.toString().contains("Count = 1"));
+        s.countDown();
+        assertTrue(s.toString().contains("Count = 0"));
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/CountedCompleterTest.java b/jdk/test/java/util/concurrent/tck/CountedCompleterTest.java
new file mode 100644
index 00000000000..8be66850920
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/CountedCompleterTest.java
@@ -0,0 +1,1872 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.util.HashSet;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountedCompleter;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class CountedCompleterTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(CountedCompleterTest.class);
+    }
+
+    // Runs with "mainPool" use > 1 thread. singletonPool tests use 1
+    static final int mainPoolSize =
+        Math.max(2, Runtime.getRuntime().availableProcessors());
+
+    private static ForkJoinPool mainPool() {
+        return new ForkJoinPool(mainPoolSize);
+    }
+
+    private static ForkJoinPool singletonPool() {
+        return new ForkJoinPool(1);
+    }
+
+    private static ForkJoinPool asyncSingletonPool() {
+        return new ForkJoinPool(1,
+                                ForkJoinPool.defaultForkJoinWorkerThreadFactory,
+                                null, true);
+    }
+
+    private void testInvokeOnPool(ForkJoinPool pool, ForkJoinTask a) {
+        try (PoolCleaner cleaner = cleaner(pool)) {
+            assertFalse(a.isDone());
+            assertFalse(a.isCompletedNormally());
+            assertFalse(a.isCompletedAbnormally());
+            assertFalse(a.isCancelled());
+            assertNull(a.getException());
+            assertNull(a.getRawResult());
+
+            assertNull(pool.invoke(a));
+
+            assertTrue(a.isDone());
+            assertTrue(a.isCompletedNormally());
+            assertFalse(a.isCompletedAbnormally());
+            assertFalse(a.isCancelled());
+            assertNull(a.getException());
+            assertNull(a.getRawResult());
+        }
+    }
+
+    void checkNotDone(CountedCompleter a) {
+        assertFalse(a.isDone());
+        assertFalse(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertFalse(a.isCancelled());
+        assertNull(a.getException());
+        assertNull(a.getRawResult());
+
+        try {
+            a.get(0L, SECONDS);
+            shouldThrow();
+        } catch (TimeoutException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCompletedNormally(CountedCompleter<?> a) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertTrue(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertNull(a.getException());
+        assertNull(a.getRawResult());
+
+        {
+            Thread.currentThread().interrupt();
+            long startTime = System.nanoTime();
+            assertNull(a.join());
+            assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
+            Thread.interrupted();
+        }
+
+        {
+            Thread.currentThread().interrupt();
+            long startTime = System.nanoTime();
+            a.quietlyJoin();        // should be no-op
+            assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
+            Thread.interrupted();
+        }
+
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+        try {
+            assertNull(a.get());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        try {
+            assertNull(a.get(5L, SECONDS));
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCancelled(CountedCompleter a) {
+        assertTrue(a.isDone());
+        assertTrue(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertTrue(a.getException() instanceof CancellationException);
+        assertNull(a.getRawResult());
+        assertTrue(a.cancel(false));
+        assertTrue(a.cancel(true));
+
+        try {
+            Thread.currentThread().interrupt();
+            a.join();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        Thread.interrupted();
+
+        {
+            long startTime = System.nanoTime();
+            a.quietlyJoin();        // should be no-op
+            assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
+        }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCompletedAbnormally(CountedCompleter a, Throwable t) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertSame(t.getClass(), a.getException().getClass());
+        assertNull(a.getRawResult());
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+
+        try {
+            Thread.currentThread().interrupt();
+            a.join();
+            shouldThrow();
+        } catch (Throwable expected) {
+            assertSame(t.getClass(), expected.getClass());
+        }
+        Thread.interrupted();
+
+        {
+            long startTime = System.nanoTime();
+            a.quietlyJoin();        // should be no-op
+            assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
+        }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.invoke();
+            shouldThrow();
+        } catch (Throwable success) {
+            assertSame(t, success);
+        }
+    }
+
+    public static final class FJException extends RuntimeException {
+        FJException() { super(); }
+    }
+
+    abstract class CheckedCC extends CountedCompleter<Object> {
+        final AtomicInteger computeN = new AtomicInteger(0);
+        final AtomicInteger onCompletionN = new AtomicInteger(0);
+        final AtomicInteger onExceptionalCompletionN = new AtomicInteger(0);
+        final AtomicInteger setRawResultN = new AtomicInteger(0);
+        final AtomicReference<Object> rawResult = new AtomicReference<Object>(null);
+        int computeN() { return computeN.get(); }
+        int onCompletionN() { return onCompletionN.get(); }
+        int onExceptionalCompletionN() { return onExceptionalCompletionN.get(); }
+        int setRawResultN() { return setRawResultN.get(); }
+
+        CheckedCC() { super(); }
+        CheckedCC(CountedCompleter p) { super(p); }
+        CheckedCC(CountedCompleter p, int n) { super(p, n); }
+        abstract void realCompute();
+        public final void compute() {
+            computeN.incrementAndGet();
+            realCompute();
+        }
+        public void onCompletion(CountedCompleter caller) {
+            onCompletionN.incrementAndGet();
+            super.onCompletion(caller);
+        }
+        public boolean onExceptionalCompletion(Throwable ex,
+                                               CountedCompleter caller) {
+            onExceptionalCompletionN.incrementAndGet();
+            assertNotNull(ex);
+            assertTrue(isCompletedAbnormally());
+            assertTrue(super.onExceptionalCompletion(ex, caller));
+            return true;
+        }
+        protected void setRawResult(Object t) {
+            setRawResultN.incrementAndGet();
+            rawResult.set(t);
+            super.setRawResult(t);
+        }
+        void checkIncomplete() {
+            assertEquals(0, computeN());
+            assertEquals(0, onCompletionN());
+            assertEquals(0, onExceptionalCompletionN());
+            assertEquals(0, setRawResultN());
+            checkNotDone(this);
+        }
+        void checkCompletes(Object rawResult) {
+            checkIncomplete();
+            int pendingCount = getPendingCount();
+            complete(rawResult);
+            assertEquals(pendingCount, getPendingCount());
+            assertEquals(0, computeN());
+            assertEquals(1, onCompletionN());
+            assertEquals(0, onExceptionalCompletionN());
+            assertEquals(1, setRawResultN());
+            assertSame(rawResult, this.rawResult.get());
+            checkCompletedNormally(this);
+        }
+        void checkCompletesExceptionally(Throwable ex) {
+            checkIncomplete();
+            completeExceptionally(ex);
+            checkCompletedExceptionally(ex);
+        }
+        void checkCompletedExceptionally(Throwable ex) {
+            assertEquals(0, computeN());
+            assertEquals(0, onCompletionN());
+            assertEquals(1, onExceptionalCompletionN());
+            assertEquals(0, setRawResultN());
+            assertNull(this.rawResult.get());
+            checkCompletedAbnormally(this, ex);
+        }
+    }
+
+    final class NoopCC extends CheckedCC {
+        NoopCC() { super(); }
+        NoopCC(CountedCompleter p) { super(p); }
+        NoopCC(CountedCompleter p, int initialPendingCount) {
+            super(p, initialPendingCount);
+        }
+        protected void realCompute() {}
+    }
+
+    /**
+     * A newly constructed CountedCompleter is not completed;
+     * complete() causes completion. pendingCount is ignored.
+     */
+    public void testComplete() {
+        for (Object x : new Object[] { Boolean.TRUE, null }) {
+            for (int pendingCount : new int[] { 0, 42 }) {
+                testComplete(new NoopCC(), x, pendingCount);
+                testComplete(new NoopCC(new NoopCC()), x, pendingCount);
+            }
+        }
+    }
+    void testComplete(NoopCC cc, Object x, int pendingCount) {
+        cc.setPendingCount(pendingCount);
+        cc.checkCompletes(x);
+        assertEquals(pendingCount, cc.getPendingCount());
+    }
+
+    /**
+     * completeExceptionally completes exceptionally
+     */
+    public void testCompleteExceptionally() {
+        new NoopCC()
+            .checkCompletesExceptionally(new FJException());
+        new NoopCC(new NoopCC())
+            .checkCompletesExceptionally(new FJException());
+    }
+
+    /**
+     * completeExceptionally(null) surprisingly has the same effect as
+     * completeExceptionally(new RuntimeException())
+     */
+    public void testCompleteExceptionally_null() {
+        NoopCC a = new NoopCC();
+        a.completeExceptionally(null);
+        try {
+            a.invoke();
+            shouldThrow();
+        } catch (RuntimeException success) {
+            assertSame(success.getClass(), RuntimeException.class);
+            assertNull(success.getCause());
+            a.checkCompletedExceptionally(success);
+        }
+    }
+
+    /**
+     * setPendingCount sets the reported pending count
+     */
+    public void testSetPendingCount() {
+        NoopCC a = new NoopCC();
+        assertEquals(0, a.getPendingCount());
+        int[] vals = {
+             -1, 0, 1,
+             Integer.MIN_VALUE,
+             Integer.MAX_VALUE,
+        };
+        for (int val : vals) {
+            a.setPendingCount(val);
+            assertEquals(val, a.getPendingCount());
+        }
+    }
+
+    /**
+     * addToPendingCount adds to the reported pending count
+     */
+    public void testAddToPendingCount() {
+        NoopCC a = new NoopCC();
+        assertEquals(0, a.getPendingCount());
+        a.addToPendingCount(1);
+        assertEquals(1, a.getPendingCount());
+        a.addToPendingCount(27);
+        assertEquals(28, a.getPendingCount());
+        a.addToPendingCount(-28);
+        assertEquals(0, a.getPendingCount());
+    }
+
+    /**
+     * decrementPendingCountUnlessZero decrements reported pending
+     * count unless zero
+     */
+    public void testDecrementPendingCountUnlessZero() {
+        NoopCC a = new NoopCC(null, 2);
+        assertEquals(2, a.getPendingCount());
+        assertEquals(2, a.decrementPendingCountUnlessZero());
+        assertEquals(1, a.getPendingCount());
+        assertEquals(1, a.decrementPendingCountUnlessZero());
+        assertEquals(0, a.getPendingCount());
+        assertEquals(0, a.decrementPendingCountUnlessZero());
+        assertEquals(0, a.getPendingCount());
+        a.setPendingCount(-1);
+        assertEquals(-1, a.decrementPendingCountUnlessZero());
+        assertEquals(-2, a.getPendingCount());
+    }
+
+    /**
+     * compareAndSetPendingCount compares and sets the reported
+     * pending count
+     */
+    public void testCompareAndSetPendingCount() {
+        NoopCC a = new NoopCC();
+        assertEquals(0, a.getPendingCount());
+        assertTrue(a.compareAndSetPendingCount(0, 1));
+        assertEquals(1, a.getPendingCount());
+        assertTrue(a.compareAndSetPendingCount(1, 2));
+        assertEquals(2, a.getPendingCount());
+        assertFalse(a.compareAndSetPendingCount(1, 3));
+        assertEquals(2, a.getPendingCount());
+    }
+
+    /**
+     * getCompleter returns parent or null if at root
+     */
+    public void testGetCompleter() {
+        NoopCC a = new NoopCC();
+        assertNull(a.getCompleter());
+        CountedCompleter b = new NoopCC(a);
+        assertSame(a, b.getCompleter());
+        CountedCompleter c = new NoopCC(b);
+        assertSame(b, c.getCompleter());
+    }
+
+    /**
+     * getRoot returns self if no parent, else parent's root
+     */
+    public void testGetRoot() {
+        NoopCC a = new NoopCC();
+        NoopCC b = new NoopCC(a);
+        NoopCC c = new NoopCC(b);
+        assertSame(a, a.getRoot());
+        assertSame(a, b.getRoot());
+        assertSame(a, c.getRoot());
+    }
+
+    /**
+     * tryComplete decrements pending count unless zero, in which case
+     * causes completion
+     */
+    public void testTryComplete() {
+        NoopCC a = new NoopCC();
+        assertEquals(0, a.getPendingCount());
+        int n = 3;
+        a.setPendingCount(n);
+        for (; n > 0; n--) {
+            assertEquals(n, a.getPendingCount());
+            a.tryComplete();
+            a.checkIncomplete();
+            assertEquals(n - 1, a.getPendingCount());
+        }
+        a.tryComplete();
+        assertEquals(0, a.computeN());
+        assertEquals(1, a.onCompletionN());
+        assertEquals(0, a.onExceptionalCompletionN());
+        assertEquals(0, a.setRawResultN());
+        checkCompletedNormally(a);
+    }
+
+    /**
+     * propagateCompletion decrements pending count unless zero, in
+     * which case causes completion, without invoking onCompletion
+     */
+    public void testPropagateCompletion() {
+        NoopCC a = new NoopCC();
+        assertEquals(0, a.getPendingCount());
+        int n = 3;
+        a.setPendingCount(n);
+        for (; n > 0; n--) {
+            assertEquals(n, a.getPendingCount());
+            a.propagateCompletion();
+            a.checkIncomplete();
+            assertEquals(n - 1, a.getPendingCount());
+        }
+        a.propagateCompletion();
+        assertEquals(0, a.computeN());
+        assertEquals(0, a.onCompletionN());
+        assertEquals(0, a.onExceptionalCompletionN());
+        assertEquals(0, a.setRawResultN());
+        checkCompletedNormally(a);
+    }
+
+    /**
+     * firstComplete returns this if pending count is zero else null
+     */
+    public void testFirstComplete() {
+        NoopCC a = new NoopCC();
+        a.setPendingCount(1);
+        assertNull(a.firstComplete());
+        a.checkIncomplete();
+        assertSame(a, a.firstComplete());
+        a.checkIncomplete();
+    }
+
+    /**
+     * firstComplete.nextComplete returns parent if pending count is
+     * zero else null
+     */
+    public void testNextComplete() {
+        NoopCC a = new NoopCC();
+        NoopCC b = new NoopCC(a);
+        a.setPendingCount(1);
+        b.setPendingCount(1);
+        assertNull(b.firstComplete());
+        assertSame(b, b.firstComplete());
+        assertNull(b.nextComplete());
+        a.checkIncomplete();
+        b.checkIncomplete();
+        assertSame(a, b.nextComplete());
+        assertSame(a, b.nextComplete());
+        a.checkIncomplete();
+        b.checkIncomplete();
+        assertNull(a.nextComplete());
+        b.checkIncomplete();
+        checkCompletedNormally(a);
+    }
+
+    /**
+     * quietlyCompleteRoot completes root task and only root task
+     */
+    public void testQuietlyCompleteRoot() {
+        NoopCC a = new NoopCC();
+        NoopCC b = new NoopCC(a);
+        NoopCC c = new NoopCC(b);
+        a.setPendingCount(1);
+        b.setPendingCount(1);
+        c.setPendingCount(1);
+        c.quietlyCompleteRoot();
+        assertTrue(a.isDone());
+        assertFalse(b.isDone());
+        assertFalse(c.isDone());
+    }
+
+    // Invocation tests use some interdependent task classes
+    // to better test propagation etc
+
+    /**
+     * Version of Fibonacci with different classes for left vs right forks
+     */
+    abstract class CCF extends CheckedCC {
+        int number;
+        int rnumber;
+
+        public CCF(CountedCompleter parent, int n) {
+            super(parent, 1);
+            this.number = n;
+        }
+
+        protected final void realCompute() {
+            CCF f = this;
+            int n = number;
+            while (n >= 2) {
+                new RCCF(f, n - 2).fork();
+                f = new LCCF(f, --n);
+            }
+            f.complete(null);
+        }
+    }
+
+    final class LCCF extends CCF {
+        public LCCF(int n) { this(null, n); }
+        public LCCF(CountedCompleter parent, int n) {
+            super(parent, n);
+        }
+        public final void onCompletion(CountedCompleter caller) {
+            super.onCompletion(caller);
+            CCF p = (CCF)getCompleter();
+            int n = number + rnumber;
+            if (p != null)
+                p.number = n;
+            else
+                number = n;
+        }
+    }
+    final class RCCF extends CCF {
+        public RCCF(CountedCompleter parent, int n) {
+            super(parent, n);
+        }
+        public final void onCompletion(CountedCompleter caller) {
+            super.onCompletion(caller);
+            CCF p = (CCF)getCompleter();
+            int n = number + rnumber;
+            if (p != null)
+                p.rnumber = n;
+            else
+                number = n;
+        }
+    }
+
+    // Version of CCF with forced failure in left completions
+    abstract class FailingCCF extends CheckedCC {
+        int number;
+        int rnumber;
+
+        public FailingCCF(CountedCompleter parent, int n) {
+            super(parent, 1);
+            this.number = n;
+        }
+
+        protected final void realCompute() {
+            FailingCCF f = this;
+            int n = number;
+            while (n >= 2) {
+                new RFCCF(f, n - 2).fork();
+                f = new LFCCF(f, --n);
+            }
+            f.complete(null);
+        }
+    }
+
+    final class LFCCF extends FailingCCF {
+        public LFCCF(int n) { this(null, n); }
+        public LFCCF(CountedCompleter parent, int n) {
+            super(parent, n);
+        }
+        public final void onCompletion(CountedCompleter caller) {
+            super.onCompletion(caller);
+            FailingCCF p = (FailingCCF)getCompleter();
+            int n = number + rnumber;
+            if (p != null)
+                p.number = n;
+            else
+                number = n;
+        }
+    }
+    final class RFCCF extends FailingCCF {
+        public RFCCF(CountedCompleter parent, int n) {
+            super(parent, n);
+        }
+        public final void onCompletion(CountedCompleter caller) {
+            super.onCompletion(caller);
+            completeExceptionally(new FJException());
+        }
+    }
+
+    /**
+     * invoke returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks; getRawResult returns null.
+     */
+    public void testInvoke() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertNull(f.invoke());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks
+     */
+    public void testQuietlyInvoke() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                f.quietlyInvoke();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join of a forked task returns when task completes
+     */
+    public void testForkJoin() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertNull(f.join());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * get of a forked task returns when task completes
+     */
+    public void testForkGet() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertNull(f.get());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get of a forked task returns when task completes
+     */
+    public void testForkTimedGet() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertNull(f.get(LONG_DELAY_MS, MILLISECONDS));
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get with null time unit throws NPE
+     */
+    public void testForkTimedGetNPE() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, null);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes
+     */
+    public void testForkQuietlyJoin() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * helpQuiesce returns when tasks are complete.
+     * getQueuedTaskCount returns 0 when quiescent
+     */
+    public void testForkHelpQuiesce() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                helpQuiesce();
+                assertEquals(21, f.number);
+                assertEquals(0, getQueuedTaskCount());
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task completes abnormally
+     */
+    public void testAbnormalInvoke() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes abnormally
+     */
+    public void testAbnormalQuietlyInvoke() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                f.quietlyInvoke();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkJoin() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkGet() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingCCF f = new LFCCF(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkTimedGet() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingCCF f = new LFCCF(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes abnormally
+     */
+    public void testAbnormalForkQuietlyJoin() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task cancelled
+     */
+    public void testCancelledInvoke() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkJoin() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkGet() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkTimedGet() throws Exception {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task cancelled
+     */
+    public void testCancelledForkQuietlyJoin() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                checkCancelled(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * getPool of executing task returns its pool
+     */
+    public void testGetPool() {
+        final ForkJoinPool mainPool = mainPool();
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertSame(mainPool, getPool());
+            }};
+        testInvokeOnPool(mainPool, a);
+    }
+
+    /**
+     * getPool of non-FJ task returns null
+     */
+    public void testGetPool2() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertNull(getPool());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * inForkJoinPool of executing task returns true
+     */
+    public void testInForkJoinPool() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertTrue(inForkJoinPool());
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * inForkJoinPool of non-FJ task returns false
+     */
+    public void testInForkJoinPool2() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertFalse(inForkJoinPool());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * setRawResult(null) succeeds
+     */
+    public void testSetRawResult() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                setRawResult(null);
+                assertNull(getRawResult());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * invoke task throws exception after invoking completeExceptionally
+     */
+    public void testCompleteExceptionally2() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF n = new LCCF(8);
+                CCF f = new LCCF(n, 8);
+                FJException ex = new FJException();
+                f.completeExceptionally(ex);
+                f.checkCompletedExceptionally(ex);
+                n.checkCompletedExceptionally(ex);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) invokes all task arguments
+     */
+    public void testInvokeAll2() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                CCF g = new LCCF(9);
+                invokeAll(f, g);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument invokes task
+     */
+    public void testInvokeAll1() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                invokeAll(f);
+                checkCompletedNormally(f);
+                assertEquals(21, f.number);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument invokes tasks
+     */
+    public void testInvokeAll3() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                CCF g = new LCCF(9);
+                CCF h = new LCCF(7);
+                invokeAll(f, g, h);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                assertEquals(13, h.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(collection) invokes all tasks in the collection
+     */
+    public void testInvokeAllCollection() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                CCF g = new LCCF(9);
+                CCF h = new LCCF(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                invokeAll(set);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                assertEquals(13, h.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with any null task throws NPE
+     */
+    public void testInvokeAllNPE() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                CCF g = new LCCF(9);
+                CCF h = null;
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) throw exception if any task does
+     */
+    public void testAbnormalInvokeAll2() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                FailingCCF g = new LFCCF(9);
+                try {
+                    invokeAll(f, g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument throws exception if task does
+     */
+    public void testAbnormalInvokeAll1() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF g = new LFCCF(9);
+                try {
+                    invokeAll(g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument throws exception if any task does
+     */
+    public void testAbnormalInvokeAll3() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                FailingCCF g = new LFCCF(9);
+                CCF h = new LCCF(7);
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(collection) throws exception if any task does
+     */
+    public void testAbnormalInvokeAllCollection() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                CCF g = new LCCF(9);
+                CCF h = new LCCF(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                try {
+                    invokeAll(set);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * tryUnfork returns true for most recent unexecuted task,
+     * and suppresses execution
+     */
+    public void testTryUnfork() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF g = new LCCF(9);
+                assertSame(g, g.fork());
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertTrue(f.tryUnfork());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * getSurplusQueuedTaskCount returns > 0 when
+     * there are more tasks than threads
+     */
+    public void testGetSurplusQueuedTaskCount() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF h = new LCCF(7);
+                assertSame(h, h.fork());
+                CCF g = new LCCF(9);
+                assertSame(g, g.fork());
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertTrue(getSurplusQueuedTaskCount() > 0);
+                helpQuiesce();
+                assertEquals(0, getSurplusQueuedTaskCount());
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * peekNextLocalTask returns most recent unexecuted task.
+     */
+    public void testPeekNextLocalTask() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF g = new LCCF(9);
+                assertSame(g, g.fork());
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertSame(f, peekNextLocalTask());
+                assertNull(f.join());
+                checkCompletedNormally(f);
+                helpQuiesce();
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * pollNextLocalTask returns most recent unexecuted task without
+     * executing it
+     */
+    public void testPollNextLocalTask() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF g = new LCCF(9);
+                assertSame(g, g.fork());
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertSame(f, pollNextLocalTask());
+                helpQuiesce();
+                checkNotDone(f);
+                assertEquals(34, g.number);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * pollTask returns an unexecuted task without executing it
+     */
+    public void testPollTask() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF g = new LCCF(9);
+                assertSame(g, g.fork());
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertSame(f, pollTask());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * peekNextLocalTask returns least recent unexecuted task in async mode
+     */
+    public void testPeekNextLocalTaskAsync() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF g = new LCCF(9);
+                assertSame(g, g.fork());
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertSame(g, peekNextLocalTask());
+                assertNull(f.join());
+                helpQuiesce();
+                checkCompletedNormally(f);
+                assertEquals(34, g.number);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    /**
+     * pollNextLocalTask returns least recent unexecuted task without
+     * executing it, in async mode
+     */
+    public void testPollNextLocalTaskAsync() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF g = new LCCF(9);
+                assertSame(g, g.fork());
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertSame(g, pollNextLocalTask());
+                helpQuiesce();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+                checkNotDone(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    /**
+     * pollTask returns an unexecuted task without executing it, in
+     * async mode
+     */
+    public void testPollTaskAsync() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF g = new LCCF(9);
+                assertSame(g, g.fork());
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertSame(g, pollTask());
+                helpQuiesce();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+                checkNotDone(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    // versions for singleton pools
+
+    /**
+     * invoke returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks; getRawResult returns null.
+     */
+    public void testInvokeSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertNull(f.invoke());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks
+     */
+    public void testQuietlyInvokeSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                f.quietlyInvoke();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * join of a forked task returns when task completes
+     */
+    public void testForkJoinSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertNull(f.join());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * get of a forked task returns when task completes
+     */
+    public void testForkGetSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertNull(f.get());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * timed get of a forked task returns when task completes
+     */
+    public void testForkTimedGetSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertNull(f.get(LONG_DELAY_MS, MILLISECONDS));
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * timed get with null time unit throws NPE
+     */
+    public void testForkTimedGetNPESingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, null);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes
+     */
+    public void testForkQuietlyJoinSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * helpQuiesce returns when tasks are complete.
+     * getQueuedTaskCount returns 0 when quiescent
+     */
+    public void testForkHelpQuiesceSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                helpQuiesce();
+                assertEquals(0, getQueuedTaskCount());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task completes abnormally
+     */
+    public void testAbnormalInvokeSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes abnormally
+     */
+    public void testAbnormalQuietlyInvokeSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                f.quietlyInvoke();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkJoinSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkGetSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingCCF f = new LFCCF(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkTimedGetSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingCCF f = new LFCCF(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes abnormally
+     */
+    public void testAbnormalForkQuietlyJoinSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task cancelled
+     */
+    public void testCancelledInvokeSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkJoinSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkGetSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkTimedGetSingleton() throws Exception {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task cancelled
+     */
+    public void testCancelledForkQuietlyJoinSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                checkCancelled(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invoke task throws exception after invoking completeExceptionally
+     */
+    public void testCompleteExceptionallySingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF n = new LCCF(8);
+                CCF f = new LCCF(n, 8);
+                FJException ex = new FJException();
+                f.completeExceptionally(ex);
+                f.checkCompletedExceptionally(ex);
+                n.checkCompletedExceptionally(ex);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) invokes all task arguments
+     */
+    public void testInvokeAll2Singleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                CCF g = new LCCF(9);
+                invokeAll(f, g);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument invokes task
+     */
+    public void testInvokeAll1Singleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                invokeAll(f);
+                checkCompletedNormally(f);
+                assertEquals(21, f.number);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument invokes tasks
+     */
+    public void testInvokeAll3Singleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                CCF g = new LCCF(9);
+                CCF h = new LCCF(7);
+                invokeAll(f, g, h);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                assertEquals(13, h.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(collection) invokes all tasks in the collection
+     */
+    public void testInvokeAllCollectionSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                CCF g = new LCCF(9);
+                CCF h = new LCCF(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                invokeAll(set);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                assertEquals(13, h.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with any null task throws NPE
+     */
+    public void testInvokeAllNPESingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                CCF g = new LCCF(9);
+                CCF h = null;
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) throw exception if any task does
+     */
+    public void testAbnormalInvokeAll2Singleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                FailingCCF g = new LFCCF(9);
+                try {
+                    invokeAll(f, g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument throws exception if task does
+     */
+    public void testAbnormalInvokeAll1Singleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF g = new LFCCF(9);
+                try {
+                    invokeAll(g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument throws exception if any task does
+     */
+    public void testAbnormalInvokeAll3Singleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                FailingCCF g = new LFCCF(9);
+                CCF h = new LCCF(7);
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(collection) throws exception if any task does
+     */
+    public void testAbnormalInvokeAllCollectionSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                CCF g = new LCCF(9);
+                CCF h = new LCCF(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                try {
+                    invokeAll(set);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/CyclicBarrierTest.java b/jdk/test/java/util/concurrent/tck/CyclicBarrierTest.java
new file mode 100644
index 00000000000..bfae2ee323b
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/CyclicBarrierTest.java
@@ -0,0 +1,491 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class CyclicBarrierTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(CyclicBarrierTest.class);
+    }
+
+    private volatile int countAction;
+    private class MyAction implements Runnable {
+        public void run() { ++countAction; }
+    }
+
+    /**
+     * Spin-waits till the number of waiters == numberOfWaiters.
+     */
+    void awaitNumberWaiting(CyclicBarrier barrier, int numberOfWaiters) {
+        long startTime = System.nanoTime();
+        while (barrier.getNumberWaiting() != numberOfWaiters) {
+            if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                fail("timed out");
+            Thread.yield();
+        }
+    }
+
+    /**
+     * Creating with negative parties throws IAE
+     */
+    public void testConstructor1() {
+        try {
+            new CyclicBarrier(-1, (Runnable)null);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Creating with negative parties and no action throws IAE
+     */
+    public void testConstructor2() {
+        try {
+            new CyclicBarrier(-1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * getParties returns the number of parties given in constructor
+     */
+    public void testGetParties() {
+        CyclicBarrier b = new CyclicBarrier(2);
+        assertEquals(2, b.getParties());
+        assertEquals(0, b.getNumberWaiting());
+    }
+
+    /**
+     * A 1-party barrier triggers after single await
+     */
+    public void testSingleParty() throws Exception {
+        CyclicBarrier b = new CyclicBarrier(1);
+        assertEquals(1, b.getParties());
+        assertEquals(0, b.getNumberWaiting());
+        b.await();
+        b.await();
+        assertEquals(0, b.getNumberWaiting());
+    }
+
+    /**
+     * The supplied barrier action is run at barrier
+     */
+    public void testBarrierAction() throws Exception {
+        countAction = 0;
+        CyclicBarrier b = new CyclicBarrier(1, new MyAction());
+        assertEquals(1, b.getParties());
+        assertEquals(0, b.getNumberWaiting());
+        b.await();
+        b.await();
+        assertEquals(0, b.getNumberWaiting());
+        assertEquals(2, countAction);
+    }
+
+    /**
+     * A 2-party/thread barrier triggers after both threads invoke await
+     */
+    public void testTwoParties() throws Exception {
+        final CyclicBarrier b = new CyclicBarrier(2);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                b.await();
+                b.await();
+                b.await();
+                b.await();
+            }});
+
+        b.await();
+        b.await();
+        b.await();
+        b.await();
+        awaitTermination(t);
+    }
+
+    /**
+     * An interruption in one party causes others waiting in await to
+     * throw BrokenBarrierException
+     */
+    public void testAwait1_Interrupted_BrokenBarrier() {
+        final CyclicBarrier c = new CyclicBarrier(3);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(2);
+        Thread t1 = new ThreadShouldThrow(InterruptedException.class) {
+            public void realRun() throws Exception {
+                pleaseInterrupt.countDown();
+                c.await();
+            }};
+        Thread t2 = new ThreadShouldThrow(BrokenBarrierException.class) {
+            public void realRun() throws Exception {
+                pleaseInterrupt.countDown();
+                c.await();
+            }};
+
+        t1.start();
+        t2.start();
+        await(pleaseInterrupt);
+        t1.interrupt();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * An interruption in one party causes others waiting in timed await to
+     * throw BrokenBarrierException
+     */
+    public void testAwait2_Interrupted_BrokenBarrier() throws Exception {
+        final CyclicBarrier c = new CyclicBarrier(3);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(2);
+        Thread t1 = new ThreadShouldThrow(InterruptedException.class) {
+            public void realRun() throws Exception {
+                pleaseInterrupt.countDown();
+                c.await(LONG_DELAY_MS, MILLISECONDS);
+            }};
+        Thread t2 = new ThreadShouldThrow(BrokenBarrierException.class) {
+            public void realRun() throws Exception {
+                pleaseInterrupt.countDown();
+                c.await(LONG_DELAY_MS, MILLISECONDS);
+            }};
+
+        t1.start();
+        t2.start();
+        await(pleaseInterrupt);
+        t1.interrupt();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * A timeout in timed await throws TimeoutException
+     */
+    public void testAwait3_TimeoutException() throws InterruptedException {
+        final CyclicBarrier c = new CyclicBarrier(2);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                long startTime = System.nanoTime();
+                try {
+                    c.await(timeoutMillis(), MILLISECONDS);
+                    shouldThrow();
+                } catch (TimeoutException success) {}
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * A timeout in one party causes others waiting in timed await to
+     * throw BrokenBarrierException
+     */
+    public void testAwait4_Timeout_BrokenBarrier() throws InterruptedException {
+        final CyclicBarrier c = new CyclicBarrier(3);
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                try {
+                    c.await(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (BrokenBarrierException success) {}
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                awaitNumberWaiting(c, 1);
+                long startTime = System.nanoTime();
+                try {
+                    c.await(timeoutMillis(), MILLISECONDS);
+                    shouldThrow();
+                } catch (TimeoutException success) {}
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            }});
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * A timeout in one party causes others waiting in await to
+     * throw BrokenBarrierException
+     */
+    public void testAwait5_Timeout_BrokenBarrier() throws InterruptedException {
+        final CyclicBarrier c = new CyclicBarrier(3);
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                try {
+                    c.await();
+                    shouldThrow();
+                } catch (BrokenBarrierException success) {}
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                awaitNumberWaiting(c, 1);
+                long startTime = System.nanoTime();
+                try {
+                    c.await(timeoutMillis(), MILLISECONDS);
+                    shouldThrow();
+                } catch (TimeoutException success) {}
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            }});
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * A reset of an active barrier causes waiting threads to throw
+     * BrokenBarrierException
+     */
+    public void testReset_BrokenBarrier() throws InterruptedException {
+        final CyclicBarrier c = new CyclicBarrier(3);
+        final CountDownLatch pleaseReset = new CountDownLatch(2);
+        Thread t1 = new ThreadShouldThrow(BrokenBarrierException.class) {
+            public void realRun() throws Exception {
+                pleaseReset.countDown();
+                c.await();
+            }};
+        Thread t2 = new ThreadShouldThrow(BrokenBarrierException.class) {
+            public void realRun() throws Exception {
+                pleaseReset.countDown();
+                c.await();
+            }};
+
+        t1.start();
+        t2.start();
+        await(pleaseReset);
+
+        awaitNumberWaiting(c, 2);
+        c.reset();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * A reset before threads enter barrier does not throw
+     * BrokenBarrierException
+     */
+    public void testReset_NoBrokenBarrier() throws Exception {
+        final CyclicBarrier c = new CyclicBarrier(3);
+        c.reset();
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                c.await();
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                c.await();
+            }});
+
+        c.await();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * All threads block while a barrier is broken.
+     */
+    public void testReset_Leakage() throws InterruptedException {
+        final CyclicBarrier c = new CyclicBarrier(2);
+        final AtomicBoolean done = new AtomicBoolean();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                while (!done.get()) {
+                    try {
+                        while (c.isBroken())
+                            c.reset();
+
+                        c.await();
+                        shouldThrow();
+                    }
+                    catch (BrokenBarrierException ok) {}
+                    catch (InterruptedException ok) {}
+                }}});
+
+        for (int i = 0; i < 4; i++) {
+            delay(timeoutMillis());
+            t.interrupt();
+        }
+        done.set(true);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * Reset of a non-broken barrier does not break barrier
+     */
+    public void testResetWithoutBreakage() throws Exception {
+        final CyclicBarrier barrier = new CyclicBarrier(3);
+        for (int i = 0; i < 3; i++) {
+            final CyclicBarrier start = new CyclicBarrier(3);
+            Thread t1 = newStartedThread(new CheckedRunnable() {
+                public void realRun() throws Exception {
+                    start.await();
+                    barrier.await();
+                }});
+
+            Thread t2 = newStartedThread(new CheckedRunnable() {
+                public void realRun() throws Exception {
+                    start.await();
+                    barrier.await();
+                }});
+
+            start.await();
+            barrier.await();
+            awaitTermination(t1);
+            awaitTermination(t2);
+            assertFalse(barrier.isBroken());
+            assertEquals(0, barrier.getNumberWaiting());
+            if (i == 1) barrier.reset();
+            assertFalse(barrier.isBroken());
+            assertEquals(0, barrier.getNumberWaiting());
+        }
+    }
+
+    /**
+     * Reset of a barrier after interruption reinitializes it.
+     */
+    public void testResetAfterInterrupt() throws Exception {
+        final CyclicBarrier barrier = new CyclicBarrier(3);
+        for (int i = 0; i < 2; i++) {
+            final CyclicBarrier start = new CyclicBarrier(3);
+            Thread t1 = new ThreadShouldThrow(InterruptedException.class) {
+                public void realRun() throws Exception {
+                    start.await();
+                    barrier.await();
+                }};
+
+            Thread t2 = new ThreadShouldThrow(BrokenBarrierException.class) {
+                public void realRun() throws Exception {
+                    start.await();
+                    barrier.await();
+                }};
+
+            t1.start();
+            t2.start();
+            start.await();
+            t1.interrupt();
+            awaitTermination(t1);
+            awaitTermination(t2);
+            assertTrue(barrier.isBroken());
+            assertEquals(0, barrier.getNumberWaiting());
+            barrier.reset();
+            assertFalse(barrier.isBroken());
+            assertEquals(0, barrier.getNumberWaiting());
+        }
+    }
+
+    /**
+     * Reset of a barrier after timeout reinitializes it.
+     */
+    public void testResetAfterTimeout() throws Exception {
+        final CyclicBarrier barrier = new CyclicBarrier(3);
+        for (int i = 0; i < 2; i++) {
+            assertEquals(0, barrier.getNumberWaiting());
+            Thread t1 = newStartedThread(new CheckedRunnable() {
+                public void realRun() throws Exception {
+                    try {
+                        barrier.await();
+                        shouldThrow();
+                    } catch (BrokenBarrierException success) {}
+                }});
+            Thread t2 = newStartedThread(new CheckedRunnable() {
+                public void realRun() throws Exception {
+                    awaitNumberWaiting(barrier, 1);
+                    long startTime = System.nanoTime();
+                    try {
+                        barrier.await(timeoutMillis(), MILLISECONDS);
+                        shouldThrow();
+                    } catch (TimeoutException success) {}
+                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                }});
+
+            awaitTermination(t1);
+            awaitTermination(t2);
+            assertEquals(0, barrier.getNumberWaiting());
+            assertTrue(barrier.isBroken());
+            assertEquals(0, barrier.getNumberWaiting());
+            barrier.reset();
+            assertFalse(barrier.isBroken());
+            assertEquals(0, barrier.getNumberWaiting());
+        }
+    }
+
+    /**
+     * Reset of a barrier after a failed command reinitializes it.
+     */
+    public void testResetAfterCommandException() throws Exception {
+        final CyclicBarrier barrier =
+            new CyclicBarrier(3, new Runnable() {
+                    public void run() {
+                        throw new NullPointerException(); }});
+        for (int i = 0; i < 2; i++) {
+            final CyclicBarrier start = new CyclicBarrier(3);
+            Thread t1 = new ThreadShouldThrow(BrokenBarrierException.class) {
+                public void realRun() throws Exception {
+                    start.await();
+                    barrier.await();
+                }};
+
+            Thread t2 = new ThreadShouldThrow(BrokenBarrierException.class) {
+                public void realRun() throws Exception {
+                    start.await();
+                    barrier.await();
+                }};
+
+            t1.start();
+            t2.start();
+            start.await();
+            awaitNumberWaiting(barrier, 2);
+            try {
+                barrier.await();
+                shouldThrow();
+            } catch (NullPointerException success) {}
+            awaitTermination(t1);
+            awaitTermination(t2);
+            assertTrue(barrier.isBroken());
+            assertEquals(0, barrier.getNumberWaiting());
+            barrier.reset();
+            assertFalse(barrier.isBroken());
+            assertEquals(0, barrier.getNumberWaiting());
+        }
+    }
+}
diff --git a/jdk/test/java/util/concurrent/tck/DelayQueueTest.java b/jdk/test/java/util/concurrent/tck/DelayQueueTest.java
new file mode 100644
index 00000000000..4d05c864554
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/DelayQueueTest.java
@@ -0,0 +1,820 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Delayed;
+import java.util.concurrent.DelayQueue;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import junit.framework.Test;
+
+public class DelayQueueTest extends JSR166TestCase {
+
+    public static class Generic extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new DelayQueue();
+        }
+        protected PDelay makeElement(int i) {
+            return new PDelay(i);
+        }
+    }
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return newTestSuite(DelayQueueTest.class,
+                            new Generic().testSuite());
+    }
+
+    /**
+     * A delayed implementation for testing.
+     * Most tests use Pseudodelays, where delays are all elapsed
+     * (so, no blocking solely for delays) but are still ordered
+     */
+    static class PDelay implements Delayed {
+        int pseudodelay;
+        PDelay(int i) { pseudodelay = i; }
+        public int compareTo(PDelay other) {
+            int a = this.pseudodelay;
+            int b = other.pseudodelay;
+            return (a < b) ? -1 : (a > b) ? 1 : 0;
+        }
+        public int compareTo(Delayed y) {
+            return compareTo((PDelay)y);
+        }
+        public boolean equals(Object other) {
+            return (other instanceof PDelay) &&
+                this.pseudodelay == ((PDelay)other).pseudodelay;
+        }
+        // suppress [overrides] javac warning
+        public int hashCode() { return pseudodelay; }
+        public long getDelay(TimeUnit ignore) {
+            return Integer.MIN_VALUE + pseudodelay;
+        }
+        public String toString() {
+            return String.valueOf(pseudodelay);
+        }
+    }
+
+    /**
+     * Delayed implementation that actually delays
+     */
+    static class NanoDelay implements Delayed {
+        long trigger;
+        NanoDelay(long i) {
+            trigger = System.nanoTime() + i;
+        }
+        public int compareTo(NanoDelay y) {
+            long i = trigger;
+            long j = y.trigger;
+            if (i < j) return -1;
+            if (i > j) return 1;
+            return 0;
+        }
+
+        public int compareTo(Delayed y) {
+            return compareTo((NanoDelay)y);
+        }
+
+        public boolean equals(Object other) {
+            return equals((NanoDelay)other);
+        }
+        public boolean equals(NanoDelay other) {
+            return other.trigger == trigger;
+        }
+
+        // suppress [overrides] javac warning
+        public int hashCode() { return (int) trigger; }
+
+        public long getDelay(TimeUnit unit) {
+            long n = trigger - System.nanoTime();
+            return unit.convert(n, TimeUnit.NANOSECONDS);
+        }
+
+        public long getTriggerTime() {
+            return trigger;
+        }
+
+        public String toString() {
+            return String.valueOf(trigger);
+        }
+    }
+
+    /**
+     * Returns a new queue of given size containing consecutive
+     * PDelays 0 ... n.
+     */
+    private DelayQueue<PDelay> populatedQueue(int n) {
+        DelayQueue<PDelay> q = new DelayQueue<PDelay>();
+        assertTrue(q.isEmpty());
+        for (int i = n - 1; i >= 0; i -= 2)
+            assertTrue(q.offer(new PDelay(i)));
+        for (int i = (n & 1); i < n; i += 2)
+            assertTrue(q.offer(new PDelay(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+        assertEquals(n, q.size());
+        return q;
+    }
+
+    /**
+     * A new queue has unbounded capacity
+     */
+    public void testConstructor1() {
+        assertEquals(Integer.MAX_VALUE, new DelayQueue().remainingCapacity());
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            new DelayQueue(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NPE
+     */
+    public void testConstructor4() {
+        try {
+            new DelayQueue(Arrays.asList(new PDelay[SIZE]));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws NPE
+     */
+    public void testConstructor5() {
+        PDelay[] a = new PDelay[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            a[i] = new PDelay(i);
+        try {
+            new DelayQueue(Arrays.asList(a));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        PDelay[] ints = new PDelay[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new PDelay(i);
+        DelayQueue q = new DelayQueue(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        DelayQueue q = new DelayQueue();
+        assertTrue(q.isEmpty());
+        assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+        q.add(new PDelay(1));
+        assertFalse(q.isEmpty());
+        q.add(new PDelay(2));
+        q.remove();
+        q.remove();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * remainingCapacity() always returns Integer.MAX_VALUE
+     */
+    public void testRemainingCapacity() {
+        BlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+            assertEquals(SIZE - i, q.size());
+            assertTrue(q.remove() instanceof PDelay);
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+            assertEquals(i, q.size());
+            assertTrue(q.add(new PDelay(i)));
+        }
+    }
+
+    /**
+     * offer non-null succeeds
+     */
+    public void testOffer() {
+        DelayQueue q = new DelayQueue();
+        assertTrue(q.offer(new PDelay(0)));
+        assertTrue(q.offer(new PDelay(1)));
+    }
+
+    /**
+     * add succeeds
+     */
+    public void testAdd() {
+        DelayQueue q = new DelayQueue();
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            assertTrue(q.add(new PDelay(i)));
+        }
+    }
+
+    /**
+     * addAll(this) throws IAE
+     */
+    public void testAddAllSelf() {
+        DelayQueue q = populatedQueue(SIZE);
+        try {
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        DelayQueue q = new DelayQueue();
+        PDelay[] a = new PDelay[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            a[i] = new PDelay(i);
+        try {
+            q.addAll(Arrays.asList(a));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of successful addAll
+     */
+    public void testAddAll5() {
+        PDelay[] empty = new PDelay[0];
+        PDelay[] ints = new PDelay[SIZE];
+        for (int i = SIZE - 1; i >= 0; --i)
+            ints[i] = new PDelay(i);
+        DelayQueue q = new DelayQueue();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * all elements successfully put are contained
+     */
+    public void testPut() {
+        DelayQueue q = new DelayQueue();
+        for (int i = 0; i < SIZE; ++i) {
+            PDelay x = new PDelay(i);
+            q.put(x);
+            assertTrue(q.contains(x));
+        }
+        assertEquals(SIZE, q.size());
+    }
+
+    /**
+     * put doesn't block waiting for take
+     */
+    public void testPutWithTake() throws InterruptedException {
+        final DelayQueue q = new DelayQueue();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                q.put(new PDelay(0));
+                q.put(new PDelay(0));
+                q.put(new PDelay(0));
+                q.put(new PDelay(0));
+            }});
+
+        awaitTermination(t);
+        assertEquals(4, q.size());
+    }
+
+    /**
+     * timed offer does not time out
+     */
+    public void testTimedOffer() throws InterruptedException {
+        final DelayQueue q = new DelayQueue();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(new PDelay(0));
+                q.put(new PDelay(0));
+                assertTrue(q.offer(new PDelay(0), SHORT_DELAY_MS, MILLISECONDS));
+                assertTrue(q.offer(new PDelay(0), LONG_DELAY_MS, MILLISECONDS));
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * take retrieves elements in priority order
+     */
+    public void testTake() throws InterruptedException {
+        DelayQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(new PDelay(i), q.take());
+        }
+    }
+
+    /**
+     * Take removes existing elements until empty, then blocks interruptibly
+     */
+    public void testBlockingTake() throws InterruptedException {
+        final DelayQueue q = populatedQueue(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(new PDelay(i), ((PDelay)q.take()));
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        DelayQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(new PDelay(i), q.poll());
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * timed poll with zero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll0() throws InterruptedException {
+        DelayQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(new PDelay(i), q.poll(0, MILLISECONDS));
+        }
+        assertNull(q.poll(0, MILLISECONDS));
+    }
+
+    /**
+     * timed poll with nonzero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll() throws InterruptedException {
+        DelayQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            long startTime = System.nanoTime();
+            assertEquals(new PDelay(i), q.poll(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+        long startTime = System.nanoTime();
+        assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        checkEmpty(q);
+    }
+
+    /**
+     * Interrupted timed poll throws InterruptedException instead of
+     * returning timeout status
+     */
+    public void testInterruptedTimedPoll() throws InterruptedException {
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        final DelayQueue q = populatedQueue(SIZE);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(new PDelay(i),
+                                 ((PDelay)q.poll(LONG_DELAY_MS, MILLISECONDS)));
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+                assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        checkEmpty(q);
+    }
+
+    /**
+     * peek returns next element, or null if empty
+     */
+    public void testPeek() {
+        DelayQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(new PDelay(i), q.peek());
+            assertEquals(new PDelay(i), q.poll());
+            if (q.isEmpty())
+                assertNull(q.peek());
+            else
+                assertFalse(new PDelay(i).equals(q.peek()));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * element returns next element, or throws NSEE if empty
+     */
+    public void testElement() {
+        DelayQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(new PDelay(i), q.element());
+            q.poll();
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        DelayQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(new PDelay(i), q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        DelayQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new PDelay(i)));
+            q.poll();
+            assertFalse(q.contains(new PDelay(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        DelayQueue q = populatedQueue(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+        PDelay x = new PDelay(1);
+        q.add(x);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(x));
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        DelayQueue q = populatedQueue(SIZE);
+        DelayQueue p = new DelayQueue();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new PDelay(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        DelayQueue q = populatedQueue(SIZE);
+        DelayQueue p = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            DelayQueue q = populatedQueue(SIZE);
+            DelayQueue p = populatedQueue(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                PDelay x = (PDelay)(p.remove());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    /**
+     * toArray contains all elements
+     */
+    public void testToArray() throws InterruptedException {
+        DelayQueue q = populatedQueue(SIZE);
+        Object[] o = q.toArray();
+        Arrays.sort(o);
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.take());
+    }
+
+    /**
+     * toArray(a) contains all elements
+     */
+    public void testToArray2() {
+        DelayQueue<PDelay> q = populatedQueue(SIZE);
+        PDelay[] ints = new PDelay[SIZE];
+        PDelay[] array = q.toArray(ints);
+        assertSame(ints, array);
+        Arrays.sort(ints);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.remove());
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray1_BadArg() {
+        DelayQueue q = populatedQueue(SIZE);
+        try {
+            q.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() {
+        DelayQueue q = populatedQueue(SIZE);
+        int i = 0;
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty collection has no elements
+     */
+    public void testEmptyIterator() {
+        assertIteratorExhausted(new DelayQueue().iterator());
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final DelayQueue q = new DelayQueue();
+        q.add(new PDelay(2));
+        q.add(new PDelay(1));
+        q.add(new PDelay(3));
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+        it = q.iterator();
+        assertEquals(new PDelay(2), it.next());
+        assertEquals(new PDelay(3), it.next());
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        DelayQueue q = populatedQueue(SIZE);
+        String s = q.toString();
+        for (Object e : q)
+            assertTrue(s.contains(e.toString()));
+    }
+
+    /**
+     * timed poll transfers elements across Executor tasks
+     */
+    public void testPollInExecutor() {
+        final DelayQueue q = new DelayQueue();
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        final ExecutorService executor = Executors.newFixedThreadPool(2);
+        try (PoolCleaner cleaner = cleaner(executor)) {
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertNull(q.poll());
+                    threadsStarted.await();
+                    assertNotNull(q.poll(LONG_DELAY_MS, MILLISECONDS));
+                    checkEmpty(q);
+                }});
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadsStarted.await();
+                    q.put(new PDelay(1));
+                }});
+        }
+    }
+
+    /**
+     * Delayed actions do not occur until their delay elapses
+     */
+    public void testDelay() throws InterruptedException {
+        DelayQueue<NanoDelay> q = new DelayQueue<NanoDelay>();
+        for (int i = 0; i < SIZE; ++i)
+            q.add(new NanoDelay(1000000L * (SIZE - i)));
+
+        long last = 0;
+        for (int i = 0; i < SIZE; ++i) {
+            NanoDelay e = q.take();
+            long tt = e.getTriggerTime();
+            assertTrue(System.nanoTime() - tt >= 0);
+            if (i != 0)
+                assertTrue(tt >= last);
+            last = tt;
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * peek of a non-empty queue returns non-null even if not expired
+     */
+    public void testPeekDelayed() {
+        DelayQueue q = new DelayQueue();
+        q.add(new NanoDelay(Long.MAX_VALUE));
+        assertNotNull(q.peek());
+    }
+
+    /**
+     * poll of a non-empty queue returns null if no expired elements.
+     */
+    public void testPollDelayed() {
+        DelayQueue q = new DelayQueue();
+        q.add(new NanoDelay(Long.MAX_VALUE));
+        assertNull(q.poll());
+    }
+
+    /**
+     * timed poll of a non-empty queue returns null if no expired elements.
+     */
+    public void testTimedPollDelayed() throws InterruptedException {
+        DelayQueue q = new DelayQueue();
+        q.add(new NanoDelay(LONG_DELAY_MS * 1000000L));
+        assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+    }
+
+    /**
+     * drainTo(c) empties queue into another collection c
+     */
+    public void testDrainTo() {
+        DelayQueue q = new DelayQueue();
+        PDelay[] elems = new PDelay[SIZE];
+        for (int i = 0; i < SIZE; ++i) {
+            elems[i] = new PDelay(i);
+            q.add(elems[i]);
+        }
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(elems[i], l.get(i));
+        q.add(elems[0]);
+        q.add(elems[1]);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(elems[0]));
+        assertTrue(q.contains(elems[1]));
+        l.clear();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(2, l.size());
+        for (int i = 0; i < 2; ++i)
+            assertEquals(elems[i], l.get(i));
+    }
+
+    /**
+     * drainTo empties queue
+     */
+    public void testDrainToWithActivePut() throws InterruptedException {
+        final DelayQueue q = populatedQueue(SIZE);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                q.put(new PDelay(SIZE + 1));
+            }});
+
+        t.start();
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertTrue(l.size() >= SIZE);
+        t.join();
+        assertTrue(q.size() + l.size() >= SIZE);
+    }
+
+    /**
+     * drainTo(c, n) empties first min(n, size) elements of queue into c
+     */
+    public void testDrainToN() {
+        for (int i = 0; i < SIZE + 2; ++i) {
+            DelayQueue q = populatedQueue(SIZE);
+            ArrayList l = new ArrayList();
+            q.drainTo(l, i);
+            int k = (i < SIZE) ? i : SIZE;
+            assertEquals(SIZE - k, q.size());
+            assertEquals(k, l.size());
+        }
+    }
+
+    /**
+     * remove(null), contains(null) always return false
+     */
+    public void testNeverContainsNull() {
+        Collection<?> q = populatedQueue(SIZE);
+        assertFalse(q.contains(null));
+        assertFalse(q.remove(null));
+    }
+}
diff --git a/jdk/test/java/util/concurrent/tck/DoubleAccumulatorTest.java b/jdk/test/java/util/concurrent/tck/DoubleAccumulatorTest.java
new file mode 100644
index 00000000000..0a7fea9eb9d
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/DoubleAccumulatorTest.java
@@ -0,0 +1,183 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Phaser;
+import java.util.concurrent.atomic.DoubleAccumulator;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class DoubleAccumulatorTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(DoubleAccumulatorTest.class);
+    }
+
+    /**
+     * default constructed initializes to zero
+     */
+    public void testConstructor() {
+        DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0);
+        assertEquals(0.0, ai.get());
+    }
+
+    /**
+     * accumulate accumulates given value to current, and get returns current value
+     */
+    public void testAccumulateAndGet() {
+        DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0);
+        ai.accumulate(2.0);
+        assertEquals(2.0, ai.get());
+        ai.accumulate(-4.0);
+        assertEquals(2.0, ai.get());
+        ai.accumulate(4.0);
+        assertEquals(4.0, ai.get());
+    }
+
+    /**
+     * reset() causes subsequent get() to return zero
+     */
+    public void testReset() {
+        DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0);
+        ai.accumulate(2.0);
+        assertEquals(2.0, ai.get());
+        ai.reset();
+        assertEquals(0.0, ai.get());
+    }
+
+    /**
+     * getThenReset() returns current value; subsequent get() returns zero
+     */
+    public void testGetThenReset() {
+        DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0);
+        ai.accumulate(2.0);
+        assertEquals(2.0, ai.get());
+        assertEquals(2.0, ai.getThenReset());
+        assertEquals(0.0, ai.get());
+    }
+
+    /**
+     * toString returns current value.
+     */
+    public void testToString() {
+        DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0);
+        assertEquals("0.0", ai.toString());
+        ai.accumulate(1.0);
+        assertEquals(Double.toString(1.0), ai.toString());
+    }
+
+    /**
+     * intValue returns current value.
+     */
+    public void testIntValue() {
+        DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0);
+        assertEquals(0, ai.intValue());
+        ai.accumulate(1.0);
+        assertEquals(1, ai.intValue());
+    }
+
+    /**
+     * longValue returns current value.
+     */
+    public void testLongValue() {
+        DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0);
+        assertEquals(0, ai.longValue());
+        ai.accumulate(1.0);
+        assertEquals(1, ai.longValue());
+    }
+
+    /**
+     * floatValue returns current value.
+     */
+    public void testFloatValue() {
+        DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0);
+        assertEquals(0.0f, ai.floatValue());
+        ai.accumulate(1.0);
+        assertEquals(1.0f, ai.floatValue());
+    }
+
+    /**
+     * doubleValue returns current value.
+     */
+    public void testDoubleValue() {
+        DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0);
+        assertEquals(0.0, ai.doubleValue());
+        ai.accumulate(1.0);
+        assertEquals(1.0, ai.doubleValue());
+    }
+
+    /**
+     * accumulates by multiple threads produce correct result
+     */
+    public void testAccumulateAndGetMT() {
+        final int incs = 1000000;
+        final int nthreads = 4;
+        final ExecutorService pool = Executors.newCachedThreadPool();
+        DoubleAccumulator a = new DoubleAccumulator(Double::max, 0.0);
+        Phaser phaser = new Phaser(nthreads + 1);
+        for (int i = 0; i < nthreads; ++i)
+            pool.execute(new AccTask(a, phaser, incs));
+        phaser.arriveAndAwaitAdvance();
+        phaser.arriveAndAwaitAdvance();
+        double expected = incs - 1;
+        double result = a.get();
+        assertEquals(expected, result);
+        pool.shutdown();
+    }
+
+    static final class AccTask implements Runnable {
+        final DoubleAccumulator acc;
+        final Phaser phaser;
+        final int incs;
+        volatile double result;
+        AccTask(DoubleAccumulator acc, Phaser phaser, int incs) {
+            this.acc = acc;
+            this.phaser = phaser;
+            this.incs = incs;
+        }
+
+        public void run() {
+            phaser.arriveAndAwaitAdvance();
+            DoubleAccumulator a = acc;
+            for (int i = 0; i < incs; ++i)
+                a.accumulate(i);
+            result = a.get();
+            phaser.arrive();
+        }
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/DoubleAdderTest.java b/jdk/test/java/util/concurrent/tck/DoubleAdderTest.java
new file mode 100644
index 00000000000..fc7d1f47834
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/DoubleAdderTest.java
@@ -0,0 +1,197 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.atomic.DoubleAdder;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class DoubleAdderTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(DoubleAdderTest.class);
+    }
+
+    /**
+     * default constructed initializes to zero
+     */
+    public void testConstructor() {
+        DoubleAdder ai = new DoubleAdder();
+        assertEquals(0.0, ai.sum());
+    }
+
+    /**
+     * add adds given value to current, and sum returns current value
+     */
+    public void testAddAndSum() {
+        DoubleAdder ai = new DoubleAdder();
+        ai.add(2.0);
+        assertEquals(2.0, ai.sum());
+        ai.add(-4.0);
+        assertEquals(-2.0, ai.sum());
+    }
+
+    /**
+     * reset() causes subsequent sum() to return zero
+     */
+    public void testReset() {
+        DoubleAdder ai = new DoubleAdder();
+        ai.add(2.0);
+        assertEquals(2.0, ai.sum());
+        ai.reset();
+        assertEquals(0.0, ai.sum());
+    }
+
+    /**
+     * sumThenReset() returns sum; subsequent sum() returns zero
+     */
+    public void testSumThenReset() {
+        DoubleAdder ai = new DoubleAdder();
+        ai.add(2.0);
+        assertEquals(2.0, ai.sum());
+        assertEquals(2.0, ai.sumThenReset());
+        assertEquals(0.0, ai.sum());
+    }
+
+    /**
+     * a deserialized serialized adder holds same value
+     */
+    public void testSerialization() throws Exception {
+        DoubleAdder x = new DoubleAdder();
+        DoubleAdder y = serialClone(x);
+        assertNotSame(x, y);
+        x.add(-22.0);
+        DoubleAdder z = serialClone(x);
+        assertEquals(-22.0, x.sum());
+        assertEquals(0.0, y.sum());
+        assertEquals(-22.0, z.sum());
+    }
+
+    /**
+     * toString returns current value.
+     */
+    public void testToString() {
+        DoubleAdder ai = new DoubleAdder();
+        assertEquals(Double.toString(0.0), ai.toString());
+        ai.add(1.0);
+        assertEquals(Double.toString(1.0), ai.toString());
+    }
+
+    /**
+     * intValue returns current value.
+     */
+    public void testIntValue() {
+        DoubleAdder ai = new DoubleAdder();
+        assertEquals(0, ai.intValue());
+        ai.add(1.0);
+        assertEquals(1, ai.intValue());
+    }
+
+    /**
+     * longValue returns current value.
+     */
+    public void testLongValue() {
+        DoubleAdder ai = new DoubleAdder();
+        assertEquals(0, ai.longValue());
+        ai.add(1.0);
+        assertEquals(1, ai.longValue());
+    }
+
+    /**
+     * floatValue returns current value.
+     */
+    public void testFloatValue() {
+        DoubleAdder ai = new DoubleAdder();
+        assertEquals(0.0f, ai.floatValue());
+        ai.add(1.0);
+        assertEquals(1.0f, ai.floatValue());
+    }
+
+    /**
+     * doubleValue returns current value.
+     */
+    public void testDoubleValue() {
+        DoubleAdder ai = new DoubleAdder();
+        assertEquals(0.0, ai.doubleValue());
+        ai.add(1.0);
+        assertEquals(1.0, ai.doubleValue());
+    }
+
+    /**
+     * adds by multiple threads produce correct sum
+     */
+    public void testAddAndSumMT() throws Throwable {
+        final int incs = 1000000;
+        final int nthreads = 4;
+        final ExecutorService pool = Executors.newCachedThreadPool();
+        DoubleAdder a = new DoubleAdder();
+        CyclicBarrier barrier = new CyclicBarrier(nthreads + 1);
+        for (int i = 0; i < nthreads; ++i)
+            pool.execute(new AdderTask(a, barrier, incs));
+        barrier.await();
+        barrier.await();
+        double total = (long)nthreads * incs;
+        double sum = a.sum();
+        assertEquals(sum, total);
+        pool.shutdown();
+    }
+
+    static final class AdderTask implements Runnable {
+        final DoubleAdder adder;
+        final CyclicBarrier barrier;
+        final int incs;
+        volatile double result;
+        AdderTask(DoubleAdder adder, CyclicBarrier barrier, int incs) {
+            this.adder = adder;
+            this.barrier = barrier;
+            this.incs = incs;
+        }
+
+        public void run() {
+            try {
+                barrier.await();
+                DoubleAdder a = adder;
+                for (int i = 0; i < incs; ++i)
+                    a.add(1.0);
+                result = a.sum();
+                barrier.await();
+            } catch (Throwable t) { throw new Error(t); }
+        }
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/EntryTest.java b/jdk/test/java/util/concurrent/tck/EntryTest.java
new file mode 100644
index 00000000000..dcd696d8d12
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/EntryTest.java
@@ -0,0 +1,157 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import java.util.AbstractMap;
+import java.util.Map;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class EntryTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(EntryTest.class);
+    }
+
+    static final String k1 = "1";
+    static final String v1 = "a";
+    static final String k2 = "2";
+    static final String v2 = "b";
+
+    /**
+     * A new SimpleEntry(k, v) holds k, v.
+     */
+    public void testConstructor1() {
+        Map.Entry e = new AbstractMap.SimpleEntry(k1, v1);
+        assertEquals(k1, e.getKey());
+        assertEquals(v1, e.getValue());
+    }
+
+    /**
+     * A new SimpleImmutableEntry(k, v) holds k, v.
+     */
+    public void testConstructor2() {
+        Map.Entry s = new AbstractMap.SimpleImmutableEntry(k1, v1);
+        assertEquals(k1, s.getKey());
+        assertEquals(v1, s.getValue());
+    }
+
+    /**
+     * A new SimpleEntry(entry(k, v)) holds k, v.
+     */
+    public void testConstructor3() {
+        Map.Entry e2 = new AbstractMap.SimpleEntry(k1, v1);
+        Map.Entry e = new AbstractMap.SimpleEntry(e2);
+        assertEquals(k1, e.getKey());
+        assertEquals(v1, e.getValue());
+    }
+
+    /**
+     * A new SimpleImmutableEntry(entry(k, v)) holds k, v.
+     */
+    public void testConstructor4() {
+        Map.Entry s2 = new AbstractMap.SimpleImmutableEntry(k1, v1);
+        Map.Entry s = new AbstractMap.SimpleImmutableEntry(s2);
+        assertEquals(k1, s.getKey());
+        assertEquals(v1, s.getValue());
+    }
+
+    /**
+     * Entries with same key-value pairs are equal and have same
+     * hashcodes
+     */
+    public void testEquals() {
+        Map.Entry e2 = new AbstractMap.SimpleEntry(k1, v1);
+        Map.Entry e = new AbstractMap.SimpleEntry(e2);
+        Map.Entry s2 = new AbstractMap.SimpleImmutableEntry(k1, v1);
+        Map.Entry s = new AbstractMap.SimpleImmutableEntry(s2);
+        assertEquals(e2, e);
+        assertEquals(e2.hashCode(), e.hashCode());
+        assertEquals(s2, s);
+        assertEquals(s2.hashCode(), s.hashCode());
+        assertEquals(e2, s2);
+        assertEquals(e2.hashCode(), s2.hashCode());
+        assertEquals(e, s);
+        assertEquals(e.hashCode(), s.hashCode());
+    }
+
+    /**
+     * Entries with different key-value pairs are not equal
+     */
+    public void testNotEquals() {
+        Map.Entry e2 = new AbstractMap.SimpleEntry(k1, v1);
+        Map.Entry e = new AbstractMap.SimpleEntry(k2, v1);
+        assertFalse(e2.equals(e));
+        e = new AbstractMap.SimpleEntry(k1, v2);
+        assertFalse(e2.equals(e));
+        e = new AbstractMap.SimpleEntry(k2, v2);
+        assertFalse(e2.equals(e));
+
+        Map.Entry s2 = new AbstractMap.SimpleImmutableEntry(k1, v1);
+        Map.Entry s = new AbstractMap.SimpleImmutableEntry(k2, v1);
+        assertFalse(s2.equals(s));
+        s = new AbstractMap.SimpleImmutableEntry(k1, v2);
+        assertFalse(s2.equals(s));
+        s = new AbstractMap.SimpleImmutableEntry(k2, v2);
+        assertFalse(s2.equals(s));
+    }
+
+    /**
+     * getValue returns last setValue for SimpleEntry
+     */
+    public void testSetValue1() {
+        Map.Entry e2 = new AbstractMap.SimpleEntry(k1, v1);
+        Map.Entry e = new AbstractMap.SimpleEntry(e2);
+        assertEquals(k1, e.getKey());
+        assertEquals(v1, e.getValue());
+        e.setValue(k2);
+        assertEquals(k2, e.getValue());
+        assertFalse(e2.equals(e));
+    }
+
+    /**
+     * setValue for SimpleImmutableEntry throws UnsupportedOperationException
+     */
+    public void testSetValue2() {
+        Map.Entry s2 = new AbstractMap.SimpleImmutableEntry(k1, v1);
+        Map.Entry s = new AbstractMap.SimpleImmutableEntry(s2);
+        assertEquals(k1, s.getKey());
+        assertEquals(v1, s.getValue());
+        try {
+            s.setValue(k2);
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+    }
+}
diff --git a/jdk/test/java/util/concurrent/tck/ExchangerTest.java b/jdk/test/java/util/concurrent/tck/ExchangerTest.java
new file mode 100644
index 00000000000..ba6c5443623
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/ExchangerTest.java
@@ -0,0 +1,180 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Exchanger;
+import java.util.concurrent.TimeoutException;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ExchangerTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ExchangerTest.class);
+    }
+
+    /**
+     * exchange exchanges objects across two threads
+     */
+    public void testExchange() {
+        final Exchanger e = new Exchanger();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertSame(one, e.exchange(two));
+                assertSame(two, e.exchange(one));
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertSame(two, e.exchange(one));
+                assertSame(one, e.exchange(two));
+            }});
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * timed exchange exchanges objects across two threads
+     */
+    public void testTimedExchange() {
+        final Exchanger e = new Exchanger();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                assertSame(one, e.exchange(two, LONG_DELAY_MS, MILLISECONDS));
+                assertSame(two, e.exchange(one, LONG_DELAY_MS, MILLISECONDS));
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                assertSame(two, e.exchange(one, LONG_DELAY_MS, MILLISECONDS));
+                assertSame(one, e.exchange(two, LONG_DELAY_MS, MILLISECONDS));
+            }});
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * interrupt during wait for exchange throws IE
+     */
+    public void testExchange_InterruptedException() {
+        final Exchanger e = new Exchanger();
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                threadStarted.countDown();
+                e.exchange(one);
+            }});
+
+        await(threadStarted);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * interrupt during wait for timed exchange throws IE
+     */
+    public void testTimedExchange_InterruptedException() {
+        final Exchanger e = new Exchanger();
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws Exception {
+                threadStarted.countDown();
+                e.exchange(null, LONG_DELAY_MS, MILLISECONDS);
+            }});
+
+        await(threadStarted);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * timeout during wait for timed exchange throws TimeoutException
+     */
+    public void testExchange_TimeoutException() {
+        final Exchanger e = new Exchanger();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                long startTime = System.nanoTime();
+                try {
+                    e.exchange(null, timeoutMillis(), MILLISECONDS);
+                    shouldThrow();
+                } catch (TimeoutException success) {}
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * If one exchanging thread is interrupted, another succeeds.
+     */
+    public void testReplacementAfterExchange() {
+        final Exchanger e = new Exchanger();
+        final CountDownLatch exchanged = new CountDownLatch(2);
+        final CountDownLatch interrupted = new CountDownLatch(1);
+        Thread t1 = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertSame(two, e.exchange(one));
+                exchanged.countDown();
+                e.exchange(two);
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertSame(one, e.exchange(two));
+                exchanged.countDown();
+                interrupted.await();
+                assertSame(three, e.exchange(one));
+            }});
+        Thread t3 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                interrupted.await();
+                assertSame(one, e.exchange(three));
+            }});
+
+        await(exchanged);
+        t1.interrupt();
+        awaitTermination(t1);
+        interrupted.countDown();
+        awaitTermination(t2);
+        awaitTermination(t3);
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/ExecutorCompletionServiceTest.java b/jdk/test/java/util/concurrent/tck/ExecutorCompletionServiceTest.java
new file mode 100644
index 00000000000..29db17a7a0d
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/ExecutorCompletionServiceTest.java
@@ -0,0 +1,241 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorCompletionService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.RunnableFuture;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ExecutorCompletionServiceTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ExecutorCompletionServiceTest.class);
+    }
+
+    /**
+     * Creating a new ECS with null Executor throw NPE
+     */
+    public void testConstructorNPE() {
+        try {
+            new ExecutorCompletionService(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Creating a new ECS with null queue throw NPE
+     */
+    public void testConstructorNPE2() {
+        try {
+            ExecutorService e = Executors.newCachedThreadPool();
+            new ExecutorCompletionService(e, null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Submitting a null callable throws NPE
+     */
+    public void testSubmitNPE() {
+        final ExecutorService e = Executors.newCachedThreadPool();
+        final ExecutorCompletionService ecs = new ExecutorCompletionService(e);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Callable c = null;
+            try {
+                ecs.submit(c);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * Submitting a null runnable throws NPE
+     */
+    public void testSubmitNPE2() {
+        final ExecutorService e = Executors.newCachedThreadPool();
+        final ExecutorCompletionService ecs = new ExecutorCompletionService(e);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Runnable r = null;
+            try {
+                ecs.submit(r, Boolean.TRUE);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * A taken submitted task is completed
+     */
+    public void testTake() throws InterruptedException {
+        final ExecutorService e = Executors.newCachedThreadPool();
+        final ExecutorCompletionService ecs = new ExecutorCompletionService(e);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Callable c = new StringTask();
+            ecs.submit(c);
+            Future f = ecs.take();
+            assertTrue(f.isDone());
+        }
+    }
+
+    /**
+     * Take returns the same future object returned by submit
+     */
+    public void testTake2() throws InterruptedException {
+        final ExecutorService e = Executors.newCachedThreadPool();
+        final ExecutorCompletionService ecs = new ExecutorCompletionService(e);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Callable c = new StringTask();
+            Future f1 = ecs.submit(c);
+            Future f2 = ecs.take();
+            assertSame(f1, f2);
+        }
+    }
+
+    /**
+     * If poll returns non-null, the returned task is completed
+     */
+    public void testPoll1() throws Exception {
+        final ExecutorService e = Executors.newCachedThreadPool();
+        final ExecutorCompletionService ecs = new ExecutorCompletionService(e);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            assertNull(ecs.poll());
+            Callable c = new StringTask();
+            ecs.submit(c);
+
+            long startTime = System.nanoTime();
+            Future f;
+            while ((f = ecs.poll()) == null) {
+                if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                    fail("timed out");
+                Thread.yield();
+            }
+            assertTrue(f.isDone());
+            assertSame(TEST_STRING, f.get());
+        }
+    }
+
+    /**
+     * If timed poll returns non-null, the returned task is completed
+     */
+    public void testPoll2() throws InterruptedException {
+        final ExecutorService e = Executors.newCachedThreadPool();
+        final ExecutorCompletionService ecs = new ExecutorCompletionService(e);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            assertNull(ecs.poll());
+            Callable c = new StringTask();
+            ecs.submit(c);
+            Future f = ecs.poll(SHORT_DELAY_MS, MILLISECONDS);
+            if (f != null)
+                assertTrue(f.isDone());
+        }
+    }
+
+    /**
+     * Submitting to underlying AES that overrides newTaskFor(Callable)
+     * returns and eventually runs Future returned by newTaskFor.
+     */
+    public void testNewTaskForCallable() throws InterruptedException {
+        final AtomicBoolean done = new AtomicBoolean(false);
+        class MyCallableFuture<V> extends FutureTask<V> {
+            MyCallableFuture(Callable<V> c) { super(c); }
+            protected void done() { done.set(true); }
+        }
+        final ExecutorService e =
+            new ThreadPoolExecutor(1, 1,
+                                   30L, TimeUnit.SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1)) {
+                protected <T> RunnableFuture<T> newTaskFor(Callable<T> c) {
+                    return new MyCallableFuture<T>(c);
+                }};
+        ExecutorCompletionService<String> ecs =
+            new ExecutorCompletionService<String>(e);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            assertNull(ecs.poll());
+            Callable<String> c = new StringTask();
+            Future f1 = ecs.submit(c);
+            assertTrue("submit must return MyCallableFuture",
+                       f1 instanceof MyCallableFuture);
+            Future f2 = ecs.take();
+            assertSame("submit and take must return same objects", f1, f2);
+            assertTrue("completed task must have set done", done.get());
+        }
+    }
+
+    /**
+     * Submitting to underlying AES that overrides newTaskFor(Runnable,T)
+     * returns and eventually runs Future returned by newTaskFor.
+     */
+    public void testNewTaskForRunnable() throws InterruptedException {
+        final AtomicBoolean done = new AtomicBoolean(false);
+        class MyRunnableFuture<V> extends FutureTask<V> {
+            MyRunnableFuture(Runnable t, V r) { super(t, r); }
+            protected void done() { done.set(true); }
+        }
+        final ExecutorService e =
+            new ThreadPoolExecutor(1, 1,
+                                   30L, TimeUnit.SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1)) {
+                protected <T> RunnableFuture<T> newTaskFor(Runnable t, T r) {
+                    return new MyRunnableFuture<T>(t, r);
+                }};
+        final ExecutorCompletionService<String> ecs =
+            new ExecutorCompletionService<String>(e);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            assertNull(ecs.poll());
+            Runnable r = new NoOpRunnable();
+            Future f1 = ecs.submit(r, null);
+            assertTrue("submit must return MyRunnableFuture",
+                       f1 instanceof MyRunnableFuture);
+            Future f2 = ecs.take();
+            assertSame("submit and take must return same objects", f1, f2);
+            assertTrue("completed task must have set done", done.get());
+        }
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/ExecutorsTest.java b/jdk/test/java/util/concurrent/tck/ExecutorsTest.java
new file mode 100644
index 00000000000..ec17b54b507
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/ExecutorsTest.java
@@ -0,0 +1,623 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.security.AccessControlContext;
+import java.security.AccessControlException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadPoolExecutor;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ExecutorsTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ExecutorsTest.class);
+    }
+
+    /**
+     * A newCachedThreadPool can execute runnables
+     */
+    public void testNewCachedThreadPool1() {
+        final ExecutorService e = Executors.newCachedThreadPool();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            e.execute(new NoOpRunnable());
+            e.execute(new NoOpRunnable());
+            e.execute(new NoOpRunnable());
+        }
+    }
+
+    /**
+     * A newCachedThreadPool with given ThreadFactory can execute runnables
+     */
+    public void testNewCachedThreadPool2() {
+        final ExecutorService e = Executors.newCachedThreadPool(new SimpleThreadFactory());
+        try (PoolCleaner cleaner = cleaner(e)) {
+            e.execute(new NoOpRunnable());
+            e.execute(new NoOpRunnable());
+            e.execute(new NoOpRunnable());
+        }
+    }
+
+    /**
+     * A newCachedThreadPool with null ThreadFactory throws NPE
+     */
+    public void testNewCachedThreadPool3() {
+        try {
+            ExecutorService e = Executors.newCachedThreadPool(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * A new SingleThreadExecutor can execute runnables
+     */
+    public void testNewSingleThreadExecutor1() {
+        final ExecutorService e = Executors.newSingleThreadExecutor();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            e.execute(new NoOpRunnable());
+            e.execute(new NoOpRunnable());
+            e.execute(new NoOpRunnable());
+        }
+    }
+
+    /**
+     * A new SingleThreadExecutor with given ThreadFactory can execute runnables
+     */
+    public void testNewSingleThreadExecutor2() {
+        final ExecutorService e = Executors.newSingleThreadExecutor(new SimpleThreadFactory());
+        try (PoolCleaner cleaner = cleaner(e)) {
+            e.execute(new NoOpRunnable());
+            e.execute(new NoOpRunnable());
+            e.execute(new NoOpRunnable());
+        }
+    }
+
+    /**
+     * A new SingleThreadExecutor with null ThreadFactory throws NPE
+     */
+    public void testNewSingleThreadExecutor3() {
+        try {
+            ExecutorService e = Executors.newSingleThreadExecutor(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * A new SingleThreadExecutor cannot be casted to concrete implementation
+     */
+    public void testCastNewSingleThreadExecutor() {
+        final ExecutorService e = Executors.newSingleThreadExecutor();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                ThreadPoolExecutor tpe = (ThreadPoolExecutor)e;
+                shouldThrow();
+            } catch (ClassCastException success) {}
+        }
+    }
+
+    /**
+     * A new newFixedThreadPool can execute runnables
+     */
+    public void testNewFixedThreadPool1() {
+        final ExecutorService e = Executors.newFixedThreadPool(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            e.execute(new NoOpRunnable());
+            e.execute(new NoOpRunnable());
+            e.execute(new NoOpRunnable());
+        }
+    }
+
+    /**
+     * A new newFixedThreadPool with given ThreadFactory can execute runnables
+     */
+    public void testNewFixedThreadPool2() {
+        final ExecutorService e = Executors.newFixedThreadPool(2, new SimpleThreadFactory());
+        try (PoolCleaner cleaner = cleaner(e)) {
+            e.execute(new NoOpRunnable());
+            e.execute(new NoOpRunnable());
+            e.execute(new NoOpRunnable());
+        }
+    }
+
+    /**
+     * A new newFixedThreadPool with null ThreadFactory throws NPE
+     */
+    public void testNewFixedThreadPool3() {
+        try {
+            ExecutorService e = Executors.newFixedThreadPool(2, null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * A new newFixedThreadPool with 0 threads throws IAE
+     */
+    public void testNewFixedThreadPool4() {
+        try {
+            ExecutorService e = Executors.newFixedThreadPool(0);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * An unconfigurable newFixedThreadPool can execute runnables
+     */
+    public void testUnconfigurableExecutorService() {
+        final ExecutorService e = Executors.unconfigurableExecutorService(Executors.newFixedThreadPool(2));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            e.execute(new NoOpRunnable());
+            e.execute(new NoOpRunnable());
+            e.execute(new NoOpRunnable());
+        }
+    }
+
+    /**
+     * unconfigurableExecutorService(null) throws NPE
+     */
+    public void testUnconfigurableExecutorServiceNPE() {
+        try {
+            ExecutorService e = Executors.unconfigurableExecutorService(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * unconfigurableScheduledExecutorService(null) throws NPE
+     */
+    public void testUnconfigurableScheduledExecutorServiceNPE() {
+        try {
+            ExecutorService e = Executors.unconfigurableScheduledExecutorService(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * a newSingleThreadScheduledExecutor successfully runs delayed task
+     */
+    public void testNewSingleThreadScheduledExecutor() throws Exception {
+        final ScheduledExecutorService p = Executors.newSingleThreadScheduledExecutor();
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch proceed = new CountDownLatch(1);
+            final Runnable task = new CheckedRunnable() {
+                public void realRun() {
+                    await(proceed);
+                }};
+            long startTime = System.nanoTime();
+            Future f = p.schedule(Executors.callable(task, Boolean.TRUE),
+                                  timeoutMillis(), MILLISECONDS);
+            assertFalse(f.isDone());
+            proceed.countDown();
+            assertSame(Boolean.TRUE, f.get(LONG_DELAY_MS, MILLISECONDS));
+            assertSame(Boolean.TRUE, f.get());
+            assertTrue(f.isDone());
+            assertFalse(f.isCancelled());
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        }
+    }
+
+    /**
+     * a newScheduledThreadPool successfully runs delayed task
+     */
+    public void testNewScheduledThreadPool() throws Exception {
+        final ScheduledExecutorService p = Executors.newScheduledThreadPool(2);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch proceed = new CountDownLatch(1);
+            final Runnable task = new CheckedRunnable() {
+                public void realRun() {
+                    await(proceed);
+                }};
+            long startTime = System.nanoTime();
+            Future f = p.schedule(Executors.callable(task, Boolean.TRUE),
+                                  timeoutMillis(), MILLISECONDS);
+            assertFalse(f.isDone());
+            proceed.countDown();
+            assertSame(Boolean.TRUE, f.get(LONG_DELAY_MS, MILLISECONDS));
+            assertSame(Boolean.TRUE, f.get());
+            assertTrue(f.isDone());
+            assertFalse(f.isCancelled());
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        }
+    }
+
+    /**
+     * an unconfigurable newScheduledThreadPool successfully runs delayed task
+     */
+    public void testUnconfigurableScheduledExecutorService() throws Exception {
+        final ScheduledExecutorService p =
+            Executors.unconfigurableScheduledExecutorService
+            (Executors.newScheduledThreadPool(2));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch proceed = new CountDownLatch(1);
+            final Runnable task = new CheckedRunnable() {
+                public void realRun() {
+                    await(proceed);
+                }};
+            long startTime = System.nanoTime();
+            Future f = p.schedule(Executors.callable(task, Boolean.TRUE),
+                                  timeoutMillis(), MILLISECONDS);
+            assertFalse(f.isDone());
+            proceed.countDown();
+            assertSame(Boolean.TRUE, f.get(LONG_DELAY_MS, MILLISECONDS));
+            assertSame(Boolean.TRUE, f.get());
+            assertTrue(f.isDone());
+            assertFalse(f.isCancelled());
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        }
+    }
+
+    /**
+     * Future.get on submitted tasks will time out if they compute too long.
+     */
+    public void testTimedCallable() throws Exception {
+        final ExecutorService[] executors = {
+            Executors.newSingleThreadExecutor(),
+            Executors.newCachedThreadPool(),
+            Executors.newFixedThreadPool(2),
+            Executors.newScheduledThreadPool(2),
+        };
+
+        final Runnable sleeper = new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                delay(LONG_DELAY_MS);
+            }};
+
+        List<Thread> threads = new ArrayList<Thread>();
+        for (final ExecutorService executor : executors) {
+            threads.add(newStartedThread(new CheckedRunnable() {
+                public void realRun() {
+                    Future future = executor.submit(sleeper);
+                    assertFutureTimesOut(future);
+                }}));
+        }
+        for (Thread thread : threads)
+            awaitTermination(thread);
+        for (ExecutorService executor : executors)
+            joinPool(executor);
+    }
+
+    /**
+     * ThreadPoolExecutor using defaultThreadFactory has
+     * specified group, priority, daemon status, and name
+     */
+    public void testDefaultThreadFactory() throws Exception {
+        final ThreadGroup egroup = Thread.currentThread().getThreadGroup();
+        final CountDownLatch done = new CountDownLatch(1);
+        Runnable r = new CheckedRunnable() {
+            public void realRun() {
+                try {
+                    Thread current = Thread.currentThread();
+                    assertTrue(!current.isDaemon());
+                    assertTrue(current.getPriority() <= Thread.NORM_PRIORITY);
+                    ThreadGroup g = current.getThreadGroup();
+                    SecurityManager s = System.getSecurityManager();
+                    if (s != null)
+                        assertTrue(g == s.getThreadGroup());
+                    else
+                        assertTrue(g == egroup);
+                    String name = current.getName();
+                    assertTrue(name.endsWith("thread-1"));
+                } catch (SecurityException ok) {
+                    // Also pass if not allowed to change setting
+                }
+                done.countDown();
+            }};
+        ExecutorService e = Executors.newSingleThreadExecutor(Executors.defaultThreadFactory());
+        try (PoolCleaner cleaner = cleaner(e)) {
+            e.execute(r);
+            await(done);
+        }
+    }
+
+    /**
+     * ThreadPoolExecutor using privilegedThreadFactory has
+     * specified group, priority, daemon status, name,
+     * access control context and context class loader
+     */
+    public void testPrivilegedThreadFactory() throws Exception {
+        final CountDownLatch done = new CountDownLatch(1);
+        Runnable r = new CheckedRunnable() {
+            public void realRun() throws Exception {
+                final ThreadGroup egroup = Thread.currentThread().getThreadGroup();
+                final ClassLoader thisccl = Thread.currentThread().getContextClassLoader();
+                final AccessControlContext thisacc = AccessController.getContext();
+                Runnable r = new CheckedRunnable() {
+                    public void realRun() {
+                        Thread current = Thread.currentThread();
+                        assertTrue(!current.isDaemon());
+                        assertTrue(current.getPriority() <= Thread.NORM_PRIORITY);
+                        ThreadGroup g = current.getThreadGroup();
+                        SecurityManager s = System.getSecurityManager();
+                        if (s != null)
+                            assertTrue(g == s.getThreadGroup());
+                        else
+                            assertTrue(g == egroup);
+                        String name = current.getName();
+                        assertTrue(name.endsWith("thread-1"));
+                        assertSame(thisccl, current.getContextClassLoader());
+                        assertEquals(thisacc, AccessController.getContext());
+                        done.countDown();
+                    }};
+                ExecutorService e = Executors.newSingleThreadExecutor(Executors.privilegedThreadFactory());
+                try (PoolCleaner cleaner = cleaner(e)) {
+                    e.execute(r);
+                    await(done);
+                }
+            }};
+
+        runWithPermissions(r,
+                           new RuntimePermission("getClassLoader"),
+                           new RuntimePermission("setContextClassLoader"),
+                           new RuntimePermission("modifyThread"));
+    }
+
+    boolean haveCCLPermissions() {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            try {
+                sm.checkPermission(new RuntimePermission("setContextClassLoader"));
+                sm.checkPermission(new RuntimePermission("getClassLoader"));
+            } catch (AccessControlException e) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    void checkCCL() {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            sm.checkPermission(new RuntimePermission("setContextClassLoader"));
+            sm.checkPermission(new RuntimePermission("getClassLoader"));
+        }
+    }
+
+    class CheckCCL implements Callable<Object> {
+        public Object call() {
+            checkCCL();
+            return null;
+        }
+    }
+
+    /**
+     * Without class loader permissions, creating
+     * privilegedCallableUsingCurrentClassLoader throws ACE
+     */
+    public void testCreatePrivilegedCallableUsingCCLWithNoPrivs() {
+        Runnable r = new CheckedRunnable() {
+            public void realRun() throws Exception {
+                if (System.getSecurityManager() == null)
+                    return;
+                try {
+                    Executors.privilegedCallableUsingCurrentClassLoader(new NoOpCallable());
+                    shouldThrow();
+                } catch (AccessControlException success) {}
+            }};
+
+        runWithoutPermissions(r);
+    }
+
+    /**
+     * With class loader permissions, calling
+     * privilegedCallableUsingCurrentClassLoader does not throw ACE
+     */
+    public void testPrivilegedCallableUsingCCLWithPrivs() throws Exception {
+        Runnable r = new CheckedRunnable() {
+            public void realRun() throws Exception {
+                Executors.privilegedCallableUsingCurrentClassLoader
+                    (new NoOpCallable())
+                    .call();
+            }};
+
+        runWithPermissions(r,
+                           new RuntimePermission("getClassLoader"),
+                           new RuntimePermission("setContextClassLoader"));
+    }
+
+    /**
+     * Without permissions, calling privilegedCallable throws ACE
+     */
+    public void testPrivilegedCallableWithNoPrivs() throws Exception {
+        // Avoid classloader-related SecurityExceptions in swingui.TestRunner
+        Executors.privilegedCallable(new CheckCCL());
+
+        Runnable r = new CheckedRunnable() {
+            public void realRun() throws Exception {
+                if (System.getSecurityManager() == null)
+                    return;
+                Callable task = Executors.privilegedCallable(new CheckCCL());
+                try {
+                    task.call();
+                    shouldThrow();
+                } catch (AccessControlException success) {}
+            }};
+
+        runWithoutPermissions(r);
+
+        // It seems rather difficult to test that the
+        // AccessControlContext of the privilegedCallable is used
+        // instead of its caller.  Below is a failed attempt to do
+        // that, which does not work because the AccessController
+        // cannot capture the internal state of the current Policy.
+        // It would be much more work to differentiate based on,
+        // e.g. CodeSource.
+
+//         final AccessControlContext[] noprivAcc = new AccessControlContext[1];
+//         final Callable[] task = new Callable[1];
+
+//         runWithPermissions
+//             (new CheckedRunnable() {
+//                 public void realRun() {
+//                     if (System.getSecurityManager() == null)
+//                         return;
+//                     noprivAcc[0] = AccessController.getContext();
+//                     task[0] = Executors.privilegedCallable(new CheckCCL());
+//                     try {
+//                         AccessController.doPrivileged(new PrivilegedAction<Void>() {
+//                                                           public Void run() {
+//                                                               checkCCL();
+//                                                               return null;
+//                                                           }}, noprivAcc[0]);
+//                         shouldThrow();
+//                     } catch (AccessControlException success) {}
+//                 }});
+
+//         runWithPermissions
+//             (new CheckedRunnable() {
+//                 public void realRun() throws Exception {
+//                     if (System.getSecurityManager() == null)
+//                         return;
+//                     // Verify that we have an underprivileged ACC
+//                     try {
+//                         AccessController.doPrivileged(new PrivilegedAction<Void>() {
+//                                                           public Void run() {
+//                                                               checkCCL();
+//                                                               return null;
+//                                                           }}, noprivAcc[0]);
+//                         shouldThrow();
+//                     } catch (AccessControlException success) {}
+
+//                     try {
+//                         task[0].call();
+//                         shouldThrow();
+//                     } catch (AccessControlException success) {}
+//                 }},
+//              new RuntimePermission("getClassLoader"),
+//              new RuntimePermission("setContextClassLoader"));
+    }
+
+    /**
+     * With permissions, calling privilegedCallable succeeds
+     */
+    public void testPrivilegedCallableWithPrivs() throws Exception {
+        Runnable r = new CheckedRunnable() {
+            public void realRun() throws Exception {
+                Executors.privilegedCallable(new CheckCCL()).call();
+            }};
+
+        runWithPermissions(r,
+                           new RuntimePermission("getClassLoader"),
+                           new RuntimePermission("setContextClassLoader"));
+    }
+
+    /**
+     * callable(Runnable) returns null when called
+     */
+    public void testCallable1() throws Exception {
+        Callable c = Executors.callable(new NoOpRunnable());
+        assertNull(c.call());
+    }
+
+    /**
+     * callable(Runnable, result) returns result when called
+     */
+    public void testCallable2() throws Exception {
+        Callable c = Executors.callable(new NoOpRunnable(), one);
+        assertSame(one, c.call());
+    }
+
+    /**
+     * callable(PrivilegedAction) returns its result when called
+     */
+    public void testCallable3() throws Exception {
+        Callable c = Executors.callable(new PrivilegedAction() {
+                public Object run() { return one; }});
+        assertSame(one, c.call());
+    }
+
+    /**
+     * callable(PrivilegedExceptionAction) returns its result when called
+     */
+    public void testCallable4() throws Exception {
+        Callable c = Executors.callable(new PrivilegedExceptionAction() {
+                public Object run() { return one; }});
+        assertSame(one, c.call());
+    }
+
+    /**
+     * callable(null Runnable) throws NPE
+     */
+    public void testCallableNPE1() {
+        try {
+            Callable c = Executors.callable((Runnable) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * callable(null, result) throws NPE
+     */
+    public void testCallableNPE2() {
+        try {
+            Callable c = Executors.callable((Runnable) null, one);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * callable(null PrivilegedAction) throws NPE
+     */
+    public void testCallableNPE3() {
+        try {
+            Callable c = Executors.callable((PrivilegedAction) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * callable(null PrivilegedExceptionAction) throws NPE
+     */
+    public void testCallableNPE4() {
+        try {
+            Callable c = Executors.callable((PrivilegedExceptionAction) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/ForkJoinPool8Test.java b/jdk/test/java/util/concurrent/tck/ForkJoinPool8Test.java
new file mode 100644
index 00000000000..ee991a21c80
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/ForkJoinPool8Test.java
@@ -0,0 +1,1615 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.util.HashSet;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountedCompleter;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.RecursiveAction;
+import java.util.concurrent.TimeoutException;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ForkJoinPool8Test extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(ForkJoinPool8Test.class);
+    }
+
+    /**
+     * Common pool exists and has expected parallelism.
+     */
+    public void testCommonPoolParallelism() {
+        assertEquals(ForkJoinPool.getCommonPoolParallelism(),
+                     ForkJoinPool.commonPool().getParallelism());
+    }
+
+    /**
+     * Common pool cannot be shut down
+     */
+    public void testCommonPoolShutDown() {
+        assertFalse(ForkJoinPool.commonPool().isShutdown());
+        assertFalse(ForkJoinPool.commonPool().isTerminating());
+        assertFalse(ForkJoinPool.commonPool().isTerminated());
+        ForkJoinPool.commonPool().shutdown();
+        assertFalse(ForkJoinPool.commonPool().isShutdown());
+        assertFalse(ForkJoinPool.commonPool().isTerminating());
+        assertFalse(ForkJoinPool.commonPool().isTerminated());
+        ForkJoinPool.commonPool().shutdownNow();
+        assertFalse(ForkJoinPool.commonPool().isShutdown());
+        assertFalse(ForkJoinPool.commonPool().isTerminating());
+        assertFalse(ForkJoinPool.commonPool().isTerminated());
+    }
+
+    /*
+     * All of the following test methods are adaptations of those for
+     * RecursiveAction and CountedCompleter, but with all actions
+     * executed in the common pool, generally implicitly via
+     * checkInvoke.
+     */
+
+    private void checkInvoke(ForkJoinTask a) {
+        checkNotDone(a);
+        assertNull(a.invoke());
+        checkCompletedNormally(a);
+    }
+
+    void checkNotDone(ForkJoinTask a) {
+        assertFalse(a.isDone());
+        assertFalse(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertFalse(a.isCancelled());
+        assertNull(a.getException());
+        assertNull(a.getRawResult());
+
+        if (! ForkJoinTask.inForkJoinPool()) {
+            Thread.currentThread().interrupt();
+            try {
+                a.get();
+                shouldThrow();
+            } catch (InterruptedException success) {
+            } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+            Thread.currentThread().interrupt();
+            try {
+                a.get(5L, SECONDS);
+                shouldThrow();
+            } catch (InterruptedException success) {
+            } catch (Throwable fail) { threadUnexpectedException(fail); }
+        }
+
+        try {
+            a.get(0L, SECONDS);
+            shouldThrow();
+        } catch (TimeoutException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCompletedNormally(ForkJoinTask a) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertTrue(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertNull(a.getException());
+        assertNull(a.getRawResult());
+        assertNull(a.join());
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+        try {
+            assertNull(a.get());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        try {
+            assertNull(a.get(5L, SECONDS));
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCancelled(ForkJoinTask a) {
+        assertTrue(a.isDone());
+        assertTrue(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertTrue(a.getException() instanceof CancellationException);
+        assertNull(a.getRawResult());
+
+        try {
+            a.join();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCompletedAbnormally(ForkJoinTask a, Throwable t) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertSame(t.getClass(), a.getException().getClass());
+        assertNull(a.getRawResult());
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+
+        try {
+            a.join();
+            shouldThrow();
+        } catch (Throwable expected) {
+            assertSame(expected.getClass(), t.getClass());
+        }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    public static final class FJException extends RuntimeException {
+        public FJException() { super(); }
+        public FJException(Throwable cause) { super(cause); }
+    }
+
+    // A simple recursive action for testing
+    final class FibAction extends CheckedRecursiveAction {
+        final int number;
+        int result;
+        FibAction(int n) { number = n; }
+        protected void realCompute() {
+            int n = number;
+            if (n <= 1)
+                result = n;
+            else {
+                FibAction f1 = new FibAction(n - 1);
+                FibAction f2 = new FibAction(n - 2);
+                invokeAll(f1, f2);
+                result = f1.result + f2.result;
+            }
+        }
+    }
+
+    // A recursive action failing in base case
+    static final class FailingFibAction extends RecursiveAction {
+        final int number;
+        int result;
+        FailingFibAction(int n) { number = n; }
+        public void compute() {
+            int n = number;
+            if (n <= 1)
+                throw new FJException();
+            else {
+                FailingFibAction f1 = new FailingFibAction(n - 1);
+                FailingFibAction f2 = new FailingFibAction(n - 2);
+                invokeAll(f1, f2);
+                result = f1.result + f2.result;
+            }
+        }
+    }
+
+    /**
+     * invoke returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks. getRawResult of a RecursiveAction returns null;
+     */
+    public void testInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertNull(f.invoke());
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks
+     */
+    public void testQuietlyInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                f.quietlyInvoke();
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * join of a forked task returns when task completes
+     */
+    public void testForkJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertNull(f.join());
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * join/quietlyJoin of a forked task succeeds in the presence of interrupts
+     */
+    public void testJoinIgnoresInterrupts() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                final Thread myself = Thread.currentThread();
+
+                // test join()
+                assertSame(f, f.fork());
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                assertNull(f.join());
+                Thread.interrupted();
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+
+                f = new FibAction(8);
+                f.cancel(true);
+                assertSame(f, f.fork());
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    Thread.interrupted();
+                    checkCancelled(f);
+                }
+
+                f = new FibAction(8);
+                f.completeExceptionally(new FJException());
+                assertSame(f, f.fork());
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    Thread.interrupted();
+                    checkCompletedAbnormally(f, success);
+                }
+
+                // test quietlyJoin()
+                f = new FibAction(8);
+                assertSame(f, f.fork());
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                f.quietlyJoin();
+                Thread.interrupted();
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+
+                f = new FibAction(8);
+                f.cancel(true);
+                assertSame(f, f.fork());
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                f.quietlyJoin();
+                Thread.interrupted();
+                checkCancelled(f);
+
+                f = new FibAction(8);
+                f.completeExceptionally(new FJException());
+                assertSame(f, f.fork());
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                f.quietlyJoin();
+                Thread.interrupted();
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        checkInvoke(a);
+        a.reinitialize();
+        checkInvoke(a);
+    }
+
+    /**
+     * get of a forked task returns when task completes
+     */
+    public void testForkGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertNull(f.get());
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * timed get of a forked task returns when task completes
+     */
+    public void testForkTimedGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertNull(f.get(5L, SECONDS));
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * timed get with null time unit throws NPE
+     */
+    public void testForkTimedGetNPE() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, null);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes
+     */
+    public void testForkQuietlyJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invoke task throws exception when task completes abnormally
+     */
+    public void testAbnormalInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes abnormally
+     */
+    public void testAbnormalQuietlyInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                f.quietlyInvoke();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * join of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingFibAction f = new FailingFibAction(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkTimedGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingFibAction f = new FailingFibAction(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, SECONDS);
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes abnormally
+     */
+    public void testAbnormalForkQuietlyJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invoke task throws exception when task cancelled
+     */
+    public void testCancelledInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertTrue(f.cancel(true));
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * join of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FibAction f = new FibAction(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkTimedGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FibAction f = new FibAction(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, SECONDS);
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task cancelled
+     */
+    public void testCancelledForkQuietlyJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                checkCancelled(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * inForkJoinPool of non-FJ task returns false
+     */
+    public void testInForkJoinPool2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertFalse(inForkJoinPool());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * A reinitialized normally completed task may be re-invoked
+     */
+    public void testReinitialize() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                checkNotDone(f);
+
+                for (int i = 0; i < 3; i++) {
+                    assertNull(f.invoke());
+                    assertEquals(21, f.result);
+                    checkCompletedNormally(f);
+                    f.reinitialize();
+                    checkNotDone(f);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * A reinitialized abnormally completed task may be re-invoked
+     */
+    public void testReinitializeAbnormal() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                checkNotDone(f);
+
+                for (int i = 0; i < 3; i++) {
+                    try {
+                        f.invoke();
+                        shouldThrow();
+                    } catch (FJException success) {
+                        checkCompletedAbnormally(f, success);
+                    }
+                    f.reinitialize();
+                    checkNotDone(f);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invoke task throws exception after invoking completeExceptionally
+     */
+    public void testCompleteExceptionally() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                f.completeExceptionally(new FJException());
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invoke task suppresses execution invoking complete
+     */
+    public void testComplete() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                f.complete(null);
+                assertNull(f.invoke());
+                assertEquals(0, f.result);
+                checkCompletedNormally(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(t1, t2) invokes all task arguments
+     */
+    public void testInvokeAll2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FibAction g = new FibAction(9);
+                invokeAll(f, g);
+                checkCompletedNormally(f);
+                assertEquals(21, f.result);
+                checkCompletedNormally(g);
+                assertEquals(34, g.result);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument invokes task
+     */
+    public void testInvokeAll1() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                invokeAll(f);
+                checkCompletedNormally(f);
+                assertEquals(21, f.result);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument invokes tasks
+     */
+    public void testInvokeAll3() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FibAction g = new FibAction(9);
+                FibAction h = new FibAction(7);
+                invokeAll(f, g, h);
+                assertTrue(f.isDone());
+                assertTrue(g.isDone());
+                assertTrue(h.isDone());
+                checkCompletedNormally(f);
+                assertEquals(21, f.result);
+                checkCompletedNormally(g);
+                assertEquals(34, g.result);
+                checkCompletedNormally(g);
+                assertEquals(13, h.result);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(collection) invokes all tasks in the collection
+     */
+    public void testInvokeAllCollection() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FibAction g = new FibAction(9);
+                FibAction h = new FibAction(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                invokeAll(set);
+                assertTrue(f.isDone());
+                assertTrue(g.isDone());
+                assertTrue(h.isDone());
+                checkCompletedNormally(f);
+                assertEquals(21, f.result);
+                checkCompletedNormally(g);
+                assertEquals(34, g.result);
+                checkCompletedNormally(g);
+                assertEquals(13, h.result);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(tasks) with any null task throws NPE
+     */
+    public void testInvokeAllNPE() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FibAction g = new FibAction(9);
+                FibAction h = null;
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(t1, t2) throw exception if any task does
+     */
+    public void testAbnormalInvokeAll2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FailingFibAction g = new FailingFibAction(9);
+                try {
+                    invokeAll(f, g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument throws exception if task does
+     */
+    public void testAbnormalInvokeAll1() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction g = new FailingFibAction(9);
+                try {
+                    invokeAll(g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument throws exception if any task does
+     */
+    public void testAbnormalInvokeAll3() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FailingFibAction g = new FailingFibAction(9);
+                FibAction h = new FibAction(7);
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(collection) throws exception if any task does
+     */
+    public void testAbnormalInvokeAllCollection() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                FibAction g = new FibAction(9);
+                FibAction h = new FibAction(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                try {
+                    invokeAll(set);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    // CountedCompleter versions
+
+    abstract static class CCF extends CountedCompleter {
+        int number;
+        int rnumber;
+
+        public CCF(CountedCompleter parent, int n) {
+            super(parent, 1);
+            this.number = n;
+        }
+
+        public final void compute() {
+            CountedCompleter p;
+            CCF f = this;
+            int n = number;
+            while (n >= 2) {
+                new RCCF(f, n - 2).fork();
+                f = new LCCF(f, --n);
+            }
+            f.number = n;
+            f.onCompletion(f);
+            if ((p = f.getCompleter()) != null)
+                p.tryComplete();
+            else
+                f.quietlyComplete();
+        }
+    }
+
+    static final class LCCF extends CCF {
+        public LCCF(CountedCompleter parent, int n) {
+            super(parent, n);
+        }
+        public final void onCompletion(CountedCompleter caller) {
+            CCF p = (CCF)getCompleter();
+            int n = number + rnumber;
+            if (p != null)
+                p.number = n;
+            else
+                number = n;
+        }
+    }
+    static final class RCCF extends CCF {
+        public RCCF(CountedCompleter parent, int n) {
+            super(parent, n);
+        }
+        public final void onCompletion(CountedCompleter caller) {
+            CCF p = (CCF)getCompleter();
+            int n = number + rnumber;
+            if (p != null)
+                p.rnumber = n;
+            else
+                number = n;
+        }
+    }
+
+    // Version of CCF with forced failure in left completions
+    abstract static class FailingCCF extends CountedCompleter {
+        int number;
+        int rnumber;
+
+        public FailingCCF(CountedCompleter parent, int n) {
+            super(parent, 1);
+            this.number = n;
+        }
+
+        public final void compute() {
+            CountedCompleter p;
+            FailingCCF f = this;
+            int n = number;
+            while (n >= 2) {
+                new RFCCF(f, n - 2).fork();
+                f = new LFCCF(f, --n);
+            }
+            f.number = n;
+            f.onCompletion(f);
+            if ((p = f.getCompleter()) != null)
+                p.tryComplete();
+            else
+                f.quietlyComplete();
+        }
+    }
+
+    static final class LFCCF extends FailingCCF {
+        public LFCCF(CountedCompleter parent, int n) {
+            super(parent, n);
+        }
+        public final void onCompletion(CountedCompleter caller) {
+            FailingCCF p = (FailingCCF)getCompleter();
+            int n = number + rnumber;
+            if (p != null)
+                p.number = n;
+            else
+                number = n;
+        }
+    }
+    static final class RFCCF extends FailingCCF {
+        public RFCCF(CountedCompleter parent, int n) {
+            super(parent, n);
+        }
+        public final void onCompletion(CountedCompleter caller) {
+            completeExceptionally(new FJException());
+        }
+    }
+
+    /**
+     * invoke returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks; getRawResult returns null.
+     */
+    public void testInvokeCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                assertNull(f.invoke());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks
+     */
+    public void testQuietlyInvokeCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                f.quietlyInvoke();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * join of a forked task returns when task completes
+     */
+    public void testForkJoinCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                assertSame(f, f.fork());
+                assertNull(f.join());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * get of a forked task returns when task completes
+     */
+    public void testForkGetCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(null, 8);
+                assertSame(f, f.fork());
+                assertNull(f.get());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * timed get of a forked task returns when task completes
+     */
+    public void testForkTimedGetCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(null, 8);
+                assertSame(f, f.fork());
+                assertNull(f.get(LONG_DELAY_MS, MILLISECONDS));
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * timed get with null time unit throws NPE
+     */
+    public void testForkTimedGetNPECC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(null, 8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, null);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes
+     */
+    public void testForkQuietlyJoinCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invoke task throws exception when task completes abnormally
+     */
+    public void testAbnormalInvokeCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(null, 8);
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes abnormally
+     */
+    public void testAbnormalQuietlyInvokeCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(null, 8);
+                f.quietlyInvoke();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * join of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkJoinCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(null, 8);
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkGetCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingCCF f = new LFCCF(null, 8);
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkTimedGetCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingCCF f = new LFCCF(null, 8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes abnormally
+     */
+    public void testAbnormalForkQuietlyJoinCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(null, 8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invoke task throws exception when task cancelled
+     */
+    public void testCancelledInvokeCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                assertTrue(f.cancel(true));
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * join of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkJoinCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkGetCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(null, 8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkTimedGetCC() throws Exception {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(null, 8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task cancelled
+     */
+    public void testCancelledForkQuietlyJoinCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                checkCancelled(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * getPool of non-FJ task returns null
+     */
+    public void testGetPool2CC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertNull(getPool());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * inForkJoinPool of non-FJ task returns false
+     */
+    public void testInForkJoinPool2CC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertFalse(inForkJoinPool());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * setRawResult(null) succeeds
+     */
+    public void testSetRawResultCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                setRawResult(null);
+                assertNull(getRawResult());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * invoke task throws exception after invoking completeExceptionally
+     */
+    public void testCompleteExceptionally2CC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                f.completeExceptionally(new FJException());
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(t1, t2) invokes all task arguments
+     */
+    public void testInvokeAll2CC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                CCF g = new LCCF(null, 9);
+                invokeAll(f, g);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument invokes task
+     */
+    public void testInvokeAll1CC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                invokeAll(f);
+                checkCompletedNormally(f);
+                assertEquals(21, f.number);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument invokes tasks
+     */
+    public void testInvokeAll3CC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                CCF g = new LCCF(null, 9);
+                CCF h = new LCCF(null, 7);
+                invokeAll(f, g, h);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                assertEquals(13, h.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(collection) invokes all tasks in the collection
+     */
+    public void testInvokeAllCollectionCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                CCF g = new LCCF(null, 9);
+                CCF h = new LCCF(null, 7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                invokeAll(set);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                assertEquals(13, h.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(tasks) with any null task throws NPE
+     */
+    public void testInvokeAllNPECC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                CCF g = new LCCF(null, 9);
+                CCF h = null;
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(t1, t2) throw exception if any task does
+     */
+    public void testAbnormalInvokeAll2CC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                FailingCCF g = new LFCCF(null, 9);
+                try {
+                    invokeAll(f, g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument throws exception if task does
+     */
+    public void testAbnormalInvokeAll1CC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF g = new LFCCF(null, 9);
+                try {
+                    invokeAll(g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument throws exception if any task does
+     */
+    public void testAbnormalInvokeAll3CC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                FailingCCF g = new LFCCF(null, 9);
+                CCF h = new LCCF(null, 7);
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(collection) throws exception if any task does
+     */
+    public void testAbnormalInvokeAllCollectionCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(null, 8);
+                CCF g = new LCCF(null, 9);
+                CCF h = new LCCF(null, 7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                try {
+                    invokeAll(set);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * awaitQuiescence by a worker is equivalent in effect to
+     * ForkJoinTask.helpQuiesce()
+     */
+    public void testAwaitQuiescence1() throws Exception {
+        final ForkJoinPool p = new ForkJoinPool();
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final long startTime = System.nanoTime();
+            assertTrue(p.isQuiescent());
+            ForkJoinTask a = new CheckedRecursiveAction() {
+                protected void realCompute() {
+                    FibAction f = new FibAction(8);
+                    assertSame(f, f.fork());
+                    assertSame(p, ForkJoinTask.getPool());
+                    boolean quiescent = p.awaitQuiescence(LONG_DELAY_MS, MILLISECONDS);
+                    assertTrue(quiescent);
+                    assertFalse(p.isQuiescent());
+                    while (!f.isDone()) {
+                        assertFalse(p.getAsyncMode());
+                        assertFalse(p.isShutdown());
+                        assertFalse(p.isTerminating());
+                        assertFalse(p.isTerminated());
+                        Thread.yield();
+                    }
+                    assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+                    assertFalse(p.isQuiescent());
+                    assertEquals(0, ForkJoinTask.getQueuedTaskCount());
+                    assertEquals(21, f.result);
+                }};
+            p.execute(a);
+            while (!a.isDone() || !p.isQuiescent()) {
+                assertFalse(p.getAsyncMode());
+                assertFalse(p.isShutdown());
+                assertFalse(p.isTerminating());
+                assertFalse(p.isTerminated());
+                Thread.yield();
+            }
+            assertEquals(0, p.getQueuedTaskCount());
+            assertFalse(p.getAsyncMode());
+            assertEquals(0, p.getQueuedSubmissionCount());
+            assertFalse(p.hasQueuedSubmissions());
+            while (p.getActiveThreadCount() != 0
+                   && millisElapsedSince(startTime) < LONG_DELAY_MS)
+                Thread.yield();
+            assertFalse(p.isShutdown());
+            assertFalse(p.isTerminating());
+            assertFalse(p.isTerminated());
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * awaitQuiescence returns when pool isQuiescent() or the indicated
+     * timeout elapsed
+     */
+    public void testAwaitQuiescence2() throws Exception {
+        /**
+         * """It is possible to disable or limit the use of threads in the
+         * common pool by setting the parallelism property to zero. However
+         * doing so may cause unjoined tasks to never be executed."""
+         */
+        if ("0".equals(System.getProperty(
+             "java.util.concurrent.ForkJoinPool.common.parallelism")))
+            return;
+        final ForkJoinPool p = new ForkJoinPool();
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertTrue(p.isQuiescent());
+            final long startTime = System.nanoTime();
+            ForkJoinTask a = new CheckedRecursiveAction() {
+                protected void realCompute() {
+                    FibAction f = new FibAction(8);
+                    assertSame(f, f.fork());
+                    while (!f.isDone()
+                           && millisElapsedSince(startTime) < LONG_DELAY_MS) {
+                        assertFalse(p.getAsyncMode());
+                        assertFalse(p.isShutdown());
+                        assertFalse(p.isTerminating());
+                        assertFalse(p.isTerminated());
+                        Thread.yield();
+                    }
+                    assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+                    assertEquals(0, ForkJoinTask.getQueuedTaskCount());
+                    assertEquals(21, f.result);
+                }};
+            p.execute(a);
+            assertTrue(p.awaitQuiescence(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(p.isQuiescent());
+            assertTrue(a.isDone());
+            assertEquals(0, p.getQueuedTaskCount());
+            assertFalse(p.getAsyncMode());
+            assertEquals(0, p.getQueuedSubmissionCount());
+            assertFalse(p.hasQueuedSubmissions());
+            while (p.getActiveThreadCount() != 0
+                   && millisElapsedSince(startTime) < LONG_DELAY_MS)
+                Thread.yield();
+            assertFalse(p.isShutdown());
+            assertFalse(p.isTerminating());
+            assertFalse(p.isTerminated());
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/ForkJoinPoolTest.java b/jdk/test/java/util/concurrent/tck/ForkJoinPoolTest.java
new file mode 100644
index 00000000000..282557d97b7
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/ForkJoinPoolTest.java
@@ -0,0 +1,991 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+import java.security.PrivilegedAction;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.ForkJoinWorkerThread;
+import java.util.concurrent.Future;
+import java.util.concurrent.RecursiveTask;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.ReentrantLock;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ForkJoinPoolTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(ForkJoinPoolTest.class);
+    }
+
+    /*
+     * Testing coverage notes:
+     *
+     * 1. shutdown and related methods are tested via super.joinPool.
+     *
+     * 2. newTaskFor and adapters are tested in submit/invoke tests
+     *
+     * 3. We cannot portably test monitoring methods such as
+     * getStealCount() since they rely ultimately on random task
+     * stealing that may cause tasks not to be stolen/propagated
+     * across threads, especially on uniprocessors.
+     *
+     * 4. There are no independently testable ForkJoinWorkerThread
+     * methods, but they are covered here and in task tests.
+     */
+
+    // Some classes to test extension and factory methods
+
+    static class MyHandler implements Thread.UncaughtExceptionHandler {
+        volatile int catches = 0;
+        public void uncaughtException(Thread t, Throwable e) {
+            ++catches;
+        }
+    }
+
+    static class MyError extends Error {}
+
+    // to test handlers
+    static class FailingFJWSubclass extends ForkJoinWorkerThread {
+        public FailingFJWSubclass(ForkJoinPool p) { super(p) ; }
+        protected void onStart() { super.onStart(); throw new MyError(); }
+    }
+
+    static class FailingThreadFactory
+            implements ForkJoinPool.ForkJoinWorkerThreadFactory {
+        volatile int calls = 0;
+        public ForkJoinWorkerThread newThread(ForkJoinPool p) {
+            if (++calls > 1) return null;
+            return new FailingFJWSubclass(p);
+        }
+    }
+
+    static class SubFJP extends ForkJoinPool { // to expose protected
+        SubFJP() { super(1); }
+        public int drainTasksTo(Collection<? super ForkJoinTask<?>> c) {
+            return super.drainTasksTo(c);
+        }
+        public ForkJoinTask<?> pollSubmission() {
+            return super.pollSubmission();
+        }
+    }
+
+    static class ManagedLocker implements ForkJoinPool.ManagedBlocker {
+        final ReentrantLock lock;
+        boolean hasLock = false;
+        ManagedLocker(ReentrantLock lock) { this.lock = lock; }
+        public boolean block() {
+            if (!hasLock)
+                lock.lock();
+            return true;
+        }
+        public boolean isReleasable() {
+            return hasLock || (hasLock = lock.tryLock());
+        }
+    }
+
+    // A simple recursive task for testing
+    static final class FibTask extends RecursiveTask<Integer> {
+        final int number;
+        FibTask(int n) { number = n; }
+        protected Integer compute() {
+            int n = number;
+            if (n <= 1)
+                return n;
+            FibTask f1 = new FibTask(n - 1);
+            f1.fork();
+            return (new FibTask(n - 2)).compute() + f1.join();
+        }
+    }
+
+    // A failing task for testing
+    static final class FailingTask extends ForkJoinTask<Void> {
+        public final Void getRawResult() { return null; }
+        protected final void setRawResult(Void mustBeNull) { }
+        protected final boolean exec() { throw new Error(); }
+        FailingTask() {}
+    }
+
+    // Fib needlessly using locking to test ManagedBlockers
+    static final class LockingFibTask extends RecursiveTask<Integer> {
+        final int number;
+        final ManagedLocker locker;
+        final ReentrantLock lock;
+        LockingFibTask(int n, ManagedLocker locker, ReentrantLock lock) {
+            number = n;
+            this.locker = locker;
+            this.lock = lock;
+        }
+        protected Integer compute() {
+            int n;
+            LockingFibTask f1 = null;
+            LockingFibTask f2 = null;
+            locker.block();
+            n = number;
+            if (n > 1) {
+                f1 = new LockingFibTask(n - 1, locker, lock);
+                f2 = new LockingFibTask(n - 2, locker, lock);
+            }
+            lock.unlock();
+            if (n <= 1)
+                return n;
+            else {
+                f1.fork();
+                return f2.compute() + f1.join();
+            }
+        }
+    }
+
+    /**
+     * Successfully constructed pool reports default factory,
+     * parallelism and async mode policies, no active threads or
+     * tasks, and quiescent running state.
+     */
+    public void testDefaultInitialState() {
+        ForkJoinPool p = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertSame(ForkJoinPool.defaultForkJoinWorkerThreadFactory,
+                       p.getFactory());
+            assertFalse(p.getAsyncMode());
+            assertEquals(0, p.getActiveThreadCount());
+            assertEquals(0, p.getStealCount());
+            assertEquals(0, p.getQueuedTaskCount());
+            assertEquals(0, p.getQueuedSubmissionCount());
+            assertFalse(p.hasQueuedSubmissions());
+            assertFalse(p.isShutdown());
+            assertFalse(p.isTerminating());
+            assertFalse(p.isTerminated());
+        }
+    }
+
+    /**
+     * Constructor throws if size argument is less than zero
+     */
+    public void testConstructor1() {
+        try {
+            new ForkJoinPool(-1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if factory argument is null
+     */
+    public void testConstructor2() {
+        try {
+            new ForkJoinPool(1, null, null, false);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * getParallelism returns size set in constructor
+     */
+    public void testGetParallelism() {
+        ForkJoinPool p = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertEquals(1, p.getParallelism());
+        }
+    }
+
+    /**
+     * getPoolSize returns number of started workers.
+     */
+    public void testGetPoolSize() {
+        final CountDownLatch taskStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        final ForkJoinPool p = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertEquals(0, p.getActiveThreadCount());
+            final Runnable task = new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    taskStarted.countDown();
+                    assertEquals(1, p.getPoolSize());
+                    assertEquals(1, p.getActiveThreadCount());
+                    done.await();
+                }};
+            Future<?> future = p.submit(task);
+            await(taskStarted);
+            assertEquals(1, p.getPoolSize());
+            assertEquals(1, p.getActiveThreadCount());
+            done.countDown();
+        }
+        assertEquals(0, p.getPoolSize());
+        assertEquals(0, p.getActiveThreadCount());
+    }
+
+    /**
+     * awaitTermination on a non-shutdown pool times out
+     */
+    public void testAwaitTermination_timesOut() throws InterruptedException {
+        ForkJoinPool p = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertFalse(p.isTerminated());
+            assertFalse(p.awaitTermination(Long.MIN_VALUE, NANOSECONDS));
+            assertFalse(p.awaitTermination(Long.MIN_VALUE, MILLISECONDS));
+            assertFalse(p.awaitTermination(-1L, NANOSECONDS));
+            assertFalse(p.awaitTermination(-1L, MILLISECONDS));
+            assertFalse(p.awaitTermination(0L, NANOSECONDS));
+            assertFalse(p.awaitTermination(0L, MILLISECONDS));
+            long timeoutNanos = 999999L;
+            long startTime = System.nanoTime();
+            assertFalse(p.awaitTermination(timeoutNanos, NANOSECONDS));
+            assertTrue(System.nanoTime() - startTime >= timeoutNanos);
+            assertFalse(p.isTerminated());
+            startTime = System.nanoTime();
+            long timeoutMillis = timeoutMillis();
+            assertFalse(p.awaitTermination(timeoutMillis, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+            assertFalse(p.isTerminated());
+            p.shutdown();
+            assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(p.isTerminated());
+        }
+    }
+
+    /**
+     * setUncaughtExceptionHandler changes handler for uncaught exceptions.
+     *
+     * Additionally tests: Overriding ForkJoinWorkerThread.onStart
+     * performs its defined action
+     */
+    public void testSetUncaughtExceptionHandler() throws InterruptedException {
+        final CountDownLatch uehInvoked = new CountDownLatch(1);
+        final Thread.UncaughtExceptionHandler ueh =
+            new Thread.UncaughtExceptionHandler() {
+                public void uncaughtException(Thread t, Throwable e) {
+                    threadAssertTrue(e instanceof MyError);
+                    threadAssertTrue(t instanceof FailingFJWSubclass);
+                    uehInvoked.countDown();
+                }};
+        ForkJoinPool p = new ForkJoinPool(1, new FailingThreadFactory(),
+                                          ueh, false);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertSame(ueh, p.getUncaughtExceptionHandler());
+            try {
+                p.execute(new FibTask(8));
+                await(uehInvoked);
+            } finally {
+                p.shutdownNow(); // failure might have prevented processing task
+            }
+        }
+    }
+
+    /**
+     * After invoking a single task, isQuiescent eventually becomes
+     * true, at which time queues are empty, threads are not active,
+     * the task has completed successfully, and construction
+     * parameters continue to hold
+     */
+    public void testIsQuiescent() throws Exception {
+        ForkJoinPool p = new ForkJoinPool(2);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertTrue(p.isQuiescent());
+            long startTime = System.nanoTime();
+            FibTask f = new FibTask(20);
+            p.invoke(f);
+            assertSame(ForkJoinPool.defaultForkJoinWorkerThreadFactory,
+                       p.getFactory());
+            while (! p.isQuiescent()) {
+                if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                    throw new AssertionFailedError("timed out");
+                assertFalse(p.getAsyncMode());
+                assertFalse(p.isShutdown());
+                assertFalse(p.isTerminating());
+                assertFalse(p.isTerminated());
+                Thread.yield();
+            }
+
+            assertTrue(p.isQuiescent());
+            assertFalse(p.getAsyncMode());
+            assertEquals(0, p.getQueuedTaskCount());
+            assertEquals(0, p.getQueuedSubmissionCount());
+            assertFalse(p.hasQueuedSubmissions());
+            while (p.getActiveThreadCount() != 0
+                   && millisElapsedSince(startTime) < LONG_DELAY_MS)
+                Thread.yield();
+            assertFalse(p.isShutdown());
+            assertFalse(p.isTerminating());
+            assertFalse(p.isTerminated());
+            assertTrue(f.isDone());
+            assertEquals(6765, (int) f.get());
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * Completed submit(ForkJoinTask) returns result
+     */
+    public void testSubmitForkJoinTask() throws Throwable {
+        ForkJoinPool p = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            ForkJoinTask<Integer> f = p.submit(new FibTask(8));
+            assertEquals(21, (int) f.get());
+        }
+    }
+
+    /**
+     * A task submitted after shutdown is rejected
+     */
+    public void testSubmitAfterShutdown() {
+        ForkJoinPool p = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            p.shutdown();
+            assertTrue(p.isShutdown());
+            try {
+                ForkJoinTask<Integer> f = p.submit(new FibTask(8));
+                shouldThrow();
+            } catch (RejectedExecutionException success) {}
+        }
+    }
+
+    /**
+     * Pool maintains parallelism when using ManagedBlocker
+     */
+    public void testBlockingForkJoinTask() throws Throwable {
+        ForkJoinPool p = new ForkJoinPool(4);
+        try {
+            ReentrantLock lock = new ReentrantLock();
+            ManagedLocker locker = new ManagedLocker(lock);
+            ForkJoinTask<Integer> f = new LockingFibTask(20, locker, lock);
+            p.execute(f);
+            assertEquals(6765, (int) f.get());
+        } finally {
+            p.shutdownNow(); // don't wait out shutdown
+        }
+    }
+
+    /**
+     * pollSubmission returns unexecuted submitted task, if present
+     */
+    public void testPollSubmission() {
+        final CountDownLatch done = new CountDownLatch(1);
+        SubFJP p = new SubFJP();
+        try (PoolCleaner cleaner = cleaner(p)) {
+            ForkJoinTask a = p.submit(awaiter(done));
+            ForkJoinTask b = p.submit(awaiter(done));
+            ForkJoinTask c = p.submit(awaiter(done));
+            ForkJoinTask r = p.pollSubmission();
+            assertTrue(r == a || r == b || r == c);
+            assertFalse(r.isDone());
+            done.countDown();
+        }
+    }
+
+    /**
+     * drainTasksTo transfers unexecuted submitted tasks, if present
+     */
+    public void testDrainTasksTo() {
+        final CountDownLatch done = new CountDownLatch(1);
+        SubFJP p = new SubFJP();
+        try (PoolCleaner cleaner = cleaner(p)) {
+            ForkJoinTask a = p.submit(awaiter(done));
+            ForkJoinTask b = p.submit(awaiter(done));
+            ForkJoinTask c = p.submit(awaiter(done));
+            ArrayList<ForkJoinTask> al = new ArrayList();
+            p.drainTasksTo(al);
+            assertTrue(al.size() > 0);
+            for (ForkJoinTask r : al) {
+                assertTrue(r == a || r == b || r == c);
+                assertFalse(r.isDone());
+            }
+            done.countDown();
+        }
+    }
+
+    // FJ Versions of AbstractExecutorService tests
+
+    /**
+     * execute(runnable) runs it to completion
+     */
+    public void testExecuteRunnable() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            final AtomicBoolean done = new AtomicBoolean(false);
+            Future<?> future = e.submit(new CheckedRunnable() {
+                public void realRun() {
+                    done.set(true);
+                }});
+            assertNull(future.get());
+            assertNull(future.get(0, MILLISECONDS));
+            assertTrue(done.get());
+            assertTrue(future.isDone());
+            assertFalse(future.isCancelled());
+        }
+    }
+
+    /**
+     * Completed submit(callable) returns result
+     */
+    public void testSubmitCallable() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<String> future = e.submit(new StringTask());
+            assertSame(TEST_STRING, future.get());
+            assertTrue(future.isDone());
+            assertFalse(future.isCancelled());
+        }
+    }
+
+    /**
+     * Completed submit(runnable) returns successfully
+     */
+    public void testSubmitRunnable() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<?> future = e.submit(new NoOpRunnable());
+            assertNull(future.get());
+            assertTrue(future.isDone());
+            assertFalse(future.isCancelled());
+        }
+    }
+
+    /**
+     * Completed submit(runnable, result) returns result
+     */
+    public void testSubmitRunnable2() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<String> future = e.submit(new NoOpRunnable(), TEST_STRING);
+            assertSame(TEST_STRING, future.get());
+            assertTrue(future.isDone());
+            assertFalse(future.isCancelled());
+        }
+    }
+
+    /**
+     * A submitted privileged action runs to completion
+     */
+    public void testSubmitPrivilegedAction() throws Exception {
+        final Callable callable = Executors.callable(new PrivilegedAction() {
+                public Object run() { return TEST_STRING; }});
+        Runnable r = new CheckedRunnable() {
+        public void realRun() throws Exception {
+            ExecutorService e = new ForkJoinPool(1);
+            try (PoolCleaner cleaner = cleaner(e)) {
+                Future future = e.submit(callable);
+                assertSame(TEST_STRING, future.get());
+            }
+        }};
+
+        runWithPermissions(r, new RuntimePermission("modifyThread"));
+    }
+
+    /**
+     * A submitted privileged exception action runs to completion
+     */
+    public void testSubmitPrivilegedExceptionAction() throws Exception {
+        final Callable callable =
+            Executors.callable(new PrivilegedExceptionAction() {
+                public Object run() { return TEST_STRING; }});
+        Runnable r = new CheckedRunnable() {
+        public void realRun() throws Exception {
+            ExecutorService e = new ForkJoinPool(1);
+            try (PoolCleaner cleaner = cleaner(e)) {
+                Future future = e.submit(callable);
+                assertSame(TEST_STRING, future.get());
+            }
+        }};
+
+        runWithPermissions(r, new RuntimePermission("modifyThread"));
+    }
+
+    /**
+     * A submitted failed privileged exception action reports exception
+     */
+    public void testSubmitFailedPrivilegedExceptionAction() throws Exception {
+        final Callable callable =
+            Executors.callable(new PrivilegedExceptionAction() {
+                public Object run() { throw new IndexOutOfBoundsException(); }});
+        Runnable r = new CheckedRunnable() {
+        public void realRun() throws Exception {
+            ExecutorService e = new ForkJoinPool(1);
+            try (PoolCleaner cleaner = cleaner(e)) {
+                Future future = e.submit(callable);
+                try {
+                    future.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    assertTrue(success.getCause() instanceof IndexOutOfBoundsException);
+                }
+            }
+        }};
+
+        runWithPermissions(r, new RuntimePermission("modifyThread"));
+    }
+
+    /**
+     * execute(null runnable) throws NullPointerException
+     */
+    public void testExecuteNullRunnable() {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                Future<?> future = e.submit((Runnable) null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * submit(null callable) throws NullPointerException
+     */
+    public void testSubmitNullCallable() {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                Future<String> future = e.submit((Callable) null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * submit(callable).get() throws InterruptedException if interrupted
+     */
+    public void testInterruptedSubmit() throws InterruptedException {
+        final CountDownLatch submitted    = new CountDownLatch(1);
+        final CountDownLatch quittingTime = new CountDownLatch(1);
+        final Callable<Void> awaiter = new CheckedCallable<Void>() {
+            public Void realCall() throws InterruptedException {
+                assertTrue(quittingTime.await(2*LONG_DELAY_MS, MILLISECONDS));
+                return null;
+            }};
+        final ExecutorService p = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(p, quittingTime)) {
+            Thread t = new Thread(new CheckedInterruptedRunnable() {
+                public void realRun() throws Exception {
+                    Future<Void> future = p.submit(awaiter);
+                    submitted.countDown();
+                    future.get();
+                }});
+            t.start();
+            await(submitted);
+            t.interrupt();
+            awaitTermination(t);
+        }
+    }
+
+    /**
+     * get of submit(callable) throws ExecutionException if callable
+     * throws exception
+     */
+    public void testSubmitEE() throws Throwable {
+        ForkJoinPool p = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.submit(new Callable() {
+                        public Object call() { throw new ArithmeticException(); }})
+                    .get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof ArithmeticException);
+            }
+        }
+    }
+
+    /**
+     * invokeAny(null) throws NullPointerException
+     */
+    public void testInvokeAny1() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * invokeAny(empty collection) throws IllegalArgumentException
+     */
+    public void testInvokeAny2() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(new ArrayList<Callable<String>>());
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * invokeAny(c) throws NullPointerException if c has a single null element
+     */
+    public void testInvokeAny3() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(null);
+            try {
+                e.invokeAny(l);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * invokeAny(c) throws NullPointerException if c has null elements
+     */
+    public void testInvokeAny4() throws Throwable {
+        CountDownLatch latch = new CountDownLatch(1);
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(latchAwaitingStringTask(latch));
+            l.add(null);
+            try {
+                e.invokeAny(l);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+            latch.countDown();
+        }
+    }
+
+    /**
+     * invokeAny(c) throws ExecutionException if no task in c completes
+     */
+    public void testInvokeAny5() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new NPETask());
+            try {
+                e.invokeAny(l);
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * invokeAny(c) returns result of some task in c if at least one completes
+     */
+    public void testInvokeAny6() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l);
+            assertSame(TEST_STRING, result);
+        }
+    }
+
+    /**
+     * invokeAll(null) throws NullPointerException
+     */
+    public void testInvokeAll1() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAll(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * invokeAll(empty collection) returns empty collection
+     */
+    public void testInvokeAll2() throws InterruptedException {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Future<String>> r
+                = e.invokeAll(new ArrayList<Callable<String>>());
+            assertTrue(r.isEmpty());
+        }
+    }
+
+    /**
+     * invokeAll(c) throws NullPointerException if c has null elements
+     */
+    public void testInvokeAll3() throws InterruptedException {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(null);
+            try {
+                e.invokeAll(l);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * get of returned element of invokeAll(c) throws
+     * ExecutionException on failed task
+     */
+    public void testInvokeAll4() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new NPETask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(1, futures.size());
+            try {
+                futures.get(0).get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * invokeAll(c) returns results of all completed tasks in c
+     */
+    public void testInvokeAll5() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        }
+    }
+
+    /**
+     * timed invokeAny(null) throws NullPointerException
+     */
+    public void testTimedInvokeAny1() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(null time unit) throws NullPointerException
+     */
+    public void testTimedInvokeAnyNullTimeUnit() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            try {
+                e.invokeAny(l, MEDIUM_DELAY_MS, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(empty collection) throws IllegalArgumentException
+     */
+    public void testTimedInvokeAny2() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(new ArrayList<Callable<String>>(),
+                            MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws NullPointerException if c has null elements
+     */
+    public void testTimedInvokeAny3() throws Throwable {
+        CountDownLatch latch = new CountDownLatch(1);
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(latchAwaitingStringTask(latch));
+            l.add(null);
+            try {
+                e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+            latch.countDown();
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testTimedInvokeAny4() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            long startTime = System.nanoTime();
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new NPETask());
+            try {
+                e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) returns result of some task in c
+     */
+    public void testTimedInvokeAny5() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            long startTime = System.nanoTime();
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+            assertSame(TEST_STRING, result);
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * timed invokeAll(null) throws NullPointerException
+     */
+    public void testTimedInvokeAll1() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAll(null time unit) throws NullPointerException
+     */
+    public void testTimedInvokeAllNullTimeUnit() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            try {
+                e.invokeAll(l, MEDIUM_DELAY_MS, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAll(empty collection) returns empty collection
+     */
+    public void testTimedInvokeAll2() throws InterruptedException {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Future<String>> r
+                = e.invokeAll(new ArrayList<Callable<String>>(),
+                              MEDIUM_DELAY_MS, MILLISECONDS);
+            assertTrue(r.isEmpty());
+        }
+    }
+
+    /**
+     * timed invokeAll(c) throws NullPointerException if c has null elements
+     */
+    public void testTimedInvokeAll3() throws InterruptedException {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(null);
+            try {
+                e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * get of returned element of invokeAll(c) throws exception on failed task
+     */
+    public void testTimedInvokeAll4() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new NPETask());
+            List<Future<String>> futures
+                = e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
+            assertEquals(1, futures.size());
+            try {
+                futures.get(0).get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * timed invokeAll(c) returns results of all completed tasks in c
+     */
+    public void testTimedInvokeAll5() throws Throwable {
+        ForkJoinPool e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures
+                = e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        }
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/ForkJoinTask8Test.java b/jdk/test/java/util/concurrent/tck/ForkJoinTask8Test.java
new file mode 100644
index 00000000000..c8fe1bb9986
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/ForkJoinTask8Test.java
@@ -0,0 +1,1228 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.ForkJoinWorkerThread;
+import java.util.concurrent.RecursiveAction;
+import java.util.concurrent.TimeoutException;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ForkJoinTask8Test extends JSR166TestCase {
+
+    /*
+     * Testing notes: This differs from ForkJoinTaskTest mainly by
+     * defining a version of BinaryAsyncAction that uses JDK8 task
+     * tags for control state, thereby testing getForkJoinTaskTag,
+     * setForkJoinTaskTag, and compareAndSetForkJoinTaskTag across
+     * various contexts. Most of the test methods using it are
+     * otherwise identical, but omitting retest of those dealing with
+     * cancellation, which is not represented in this tag scheme.
+     */
+
+    static final short INITIAL_STATE = -1;
+    static final short COMPLETE_STATE = 0;
+    static final short EXCEPTION_STATE = 1;
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(ForkJoinTask8Test.class);
+    }
+
+    // Runs with "mainPool" use > 1 thread. singletonPool tests use 1
+    static final int mainPoolSize =
+        Math.max(2, Runtime.getRuntime().availableProcessors());
+
+    private static ForkJoinPool mainPool() {
+        return new ForkJoinPool(mainPoolSize);
+    }
+
+    private static ForkJoinPool singletonPool() {
+        return new ForkJoinPool(1);
+    }
+
+    private static ForkJoinPool asyncSingletonPool() {
+        return new ForkJoinPool(1,
+                                ForkJoinPool.defaultForkJoinWorkerThreadFactory,
+                                null, true);
+    }
+
+    // Compute fib naively and efficiently
+    final int[] fib;
+    {
+        int[] fib = new int[10];
+        fib[0] = 0;
+        fib[1] = 1;
+        for (int i = 2; i < fib.length; i++)
+            fib[i] = fib[i - 1] + fib[i - 2];
+        this.fib = fib;
+    }
+
+    private void testInvokeOnPool(ForkJoinPool pool, RecursiveAction a) {
+        try (PoolCleaner cleaner = cleaner(pool)) {
+            assertFalse(a.isDone());
+            assertFalse(a.isCompletedNormally());
+            assertFalse(a.isCompletedAbnormally());
+            assertFalse(a.isCancelled());
+            assertNull(a.getException());
+            assertNull(a.getRawResult());
+
+            assertNull(pool.invoke(a));
+
+            assertTrue(a.isDone());
+            assertTrue(a.isCompletedNormally());
+            assertFalse(a.isCompletedAbnormally());
+            assertFalse(a.isCancelled());
+            assertNull(a.getException());
+            assertNull(a.getRawResult());
+        }
+    }
+
+    void checkNotDone(ForkJoinTask a) {
+        assertFalse(a.isDone());
+        assertFalse(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertFalse(a.isCancelled());
+        assertNull(a.getException());
+        assertNull(a.getRawResult());
+        if (a instanceof BinaryAsyncAction)
+            assertTrue(((BinaryAsyncAction)a).getForkJoinTaskTag() == INITIAL_STATE);
+
+        try {
+            a.get(0L, SECONDS);
+            shouldThrow();
+        } catch (TimeoutException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    <T> void checkCompletedNormally(ForkJoinTask<T> a) {
+        checkCompletedNormally(a, null);
+    }
+
+    <T> void checkCompletedNormally(ForkJoinTask<T> a, T expected) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertTrue(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertNull(a.getException());
+        assertSame(expected, a.getRawResult());
+        if (a instanceof BinaryAsyncAction)
+            assertTrue(((BinaryAsyncAction)a).getForkJoinTaskTag() == COMPLETE_STATE);
+
+        {
+            Thread.currentThread().interrupt();
+            long startTime = System.nanoTime();
+            assertSame(expected, a.join());
+            assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
+            Thread.interrupted();
+        }
+
+        {
+            Thread.currentThread().interrupt();
+            long startTime = System.nanoTime();
+            a.quietlyJoin();        // should be no-op
+            assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
+            Thread.interrupted();
+        }
+
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+        try {
+            assertSame(expected, a.get());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        try {
+            assertSame(expected, a.get(5L, SECONDS));
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCompletedAbnormally(ForkJoinTask a, Throwable t) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertSame(t.getClass(), a.getException().getClass());
+        assertNull(a.getRawResult());
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+        if (a instanceof BinaryAsyncAction)
+            assertTrue(((BinaryAsyncAction)a).getForkJoinTaskTag() != INITIAL_STATE);
+
+        try {
+            Thread.currentThread().interrupt();
+            a.join();
+            shouldThrow();
+        } catch (Throwable expected) {
+            assertSame(t.getClass(), expected.getClass());
+        }
+        Thread.interrupted();
+
+        {
+            long startTime = System.nanoTime();
+            a.quietlyJoin();        // should be no-op
+            assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
+        }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    public static final class FJException extends RuntimeException {
+        FJException() { super(); }
+    }
+
+    abstract static class BinaryAsyncAction extends ForkJoinTask<Void> {
+
+        private volatile BinaryAsyncAction parent;
+
+        private volatile BinaryAsyncAction sibling;
+
+        protected BinaryAsyncAction() {
+            setForkJoinTaskTag(INITIAL_STATE);
+        }
+
+        public final Void getRawResult() { return null; }
+        protected final void setRawResult(Void mustBeNull) { }
+
+        public final void linkSubtasks(BinaryAsyncAction x, BinaryAsyncAction y) {
+            x.parent = y.parent = this;
+            x.sibling = y;
+            y.sibling = x;
+        }
+
+        protected void onComplete(BinaryAsyncAction x, BinaryAsyncAction y) {
+            if (this.getForkJoinTaskTag() != COMPLETE_STATE ||
+                x.getForkJoinTaskTag() != COMPLETE_STATE ||
+                y.getForkJoinTaskTag() != COMPLETE_STATE) {
+                completeThisExceptionally(new FJException());
+            }
+        }
+
+        protected boolean onException() {
+            return true;
+        }
+
+        public void linkAndForkSubtasks(BinaryAsyncAction x, BinaryAsyncAction y) {
+            linkSubtasks(x, y);
+            y.fork();
+            x.fork();
+        }
+
+        private void completeThis() {
+            setForkJoinTaskTag(COMPLETE_STATE);
+            super.complete(null);
+        }
+
+        private void completeThisExceptionally(Throwable ex) {
+            setForkJoinTaskTag(EXCEPTION_STATE);
+            super.completeExceptionally(ex);
+        }
+
+        public boolean cancel(boolean mayInterruptIfRunning) {
+            if (super.cancel(mayInterruptIfRunning)) {
+                completeExceptionally(new FJException());
+                return true;
+            }
+            return false;
+        }
+
+        public final void complete() {
+            BinaryAsyncAction a = this;
+            for (;;) {
+                BinaryAsyncAction s = a.sibling;
+                BinaryAsyncAction p = a.parent;
+                a.sibling = null;
+                a.parent = null;
+                a.completeThis();
+                if (p == null ||
+                    p.compareAndSetForkJoinTaskTag(INITIAL_STATE, COMPLETE_STATE))
+                    break;
+                try {
+                    p.onComplete(a, s);
+                } catch (Throwable rex) {
+                    p.completeExceptionally(rex);
+                    return;
+                }
+                a = p;
+            }
+        }
+
+        public final void completeExceptionally(Throwable ex) {
+            for (BinaryAsyncAction a = this;;) {
+                a.completeThisExceptionally(ex);
+                BinaryAsyncAction s = a.sibling;
+                if (s != null && !s.isDone())
+                    s.completeExceptionally(ex);
+                if ((a = a.parent) == null)
+                    break;
+            }
+        }
+
+        public final BinaryAsyncAction getParent() {
+            return parent;
+        }
+
+        public BinaryAsyncAction getSibling() {
+            return sibling;
+        }
+
+        public void reinitialize() {
+            parent = sibling = null;
+            super.reinitialize();
+        }
+
+    }
+
+    final class AsyncFib extends BinaryAsyncAction {
+        int number;
+        int expectedResult;
+        public AsyncFib(int number) {
+            this.number = number;
+            this.expectedResult = fib[number];
+        }
+
+        public final boolean exec() {
+            try {
+                AsyncFib f = this;
+                int n = f.number;
+                while (n > 1) {
+                    AsyncFib p = f;
+                    AsyncFib r = new AsyncFib(n - 2);
+                    f = new AsyncFib(--n);
+                    p.linkSubtasks(r, f);
+                    r.fork();
+                }
+                f.complete();
+            }
+            catch (Throwable ex) {
+                compareAndSetForkJoinTaskTag(INITIAL_STATE, EXCEPTION_STATE);
+            }
+            if (getForkJoinTaskTag() == EXCEPTION_STATE)
+                throw new FJException();
+            return false;
+        }
+
+        protected void onComplete(BinaryAsyncAction x, BinaryAsyncAction y) {
+            number = ((AsyncFib)x).number + ((AsyncFib)y).number;
+            super.onComplete(x, y);
+        }
+
+        public void checkCompletedNormally() {
+            assertEquals(expectedResult, number);
+            ForkJoinTask8Test.this.checkCompletedNormally(this);
+        }
+    }
+
+    static final class FailingAsyncFib extends BinaryAsyncAction {
+        int number;
+        public FailingAsyncFib(int n) {
+            this.number = n;
+        }
+
+        public final boolean exec() {
+            try {
+                FailingAsyncFib f = this;
+                int n = f.number;
+                while (n > 1) {
+                    FailingAsyncFib p = f;
+                    FailingAsyncFib r = new FailingAsyncFib(n - 2);
+                    f = new FailingAsyncFib(--n);
+                    p.linkSubtasks(r, f);
+                    r.fork();
+                }
+                f.complete();
+            }
+            catch (Throwable ex) {
+                compareAndSetForkJoinTaskTag(INITIAL_STATE, EXCEPTION_STATE);
+            }
+            if (getForkJoinTaskTag() == EXCEPTION_STATE)
+                throw new FJException();
+            return false;
+        }
+
+        protected void onComplete(BinaryAsyncAction x, BinaryAsyncAction y) {
+            completeExceptionally(new FJException());
+        }
+    }
+
+    /**
+     * invoke returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks; getRawResult returns null.
+     */
+    public void testInvoke() {
+        testInvoke(mainPool());
+    }
+    public void testInvoke_Singleton() {
+        testInvoke(singletonPool());
+    }
+    public void testInvoke(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertNull(f.invoke());
+                f.checkCompletedNormally();
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks
+     */
+    public void testQuietlyInvoke() {
+        testQuietlyInvoke(mainPool());
+    }
+    public void testQuietlyInvoke_Singleton() {
+        testQuietlyInvoke(singletonPool());
+    }
+    public void testQuietlyInvoke(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                f.quietlyInvoke();
+                f.checkCompletedNormally();
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * join of a forked task returns when task completes
+     */
+    public void testForkJoin() {
+        testForkJoin(mainPool());
+    }
+    public void testForkJoin_Singleton() {
+        testForkJoin(singletonPool());
+    }
+    public void testForkJoin(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertNull(f.join());
+                f.checkCompletedNormally();
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * get of a forked task returns when task completes
+     */
+    public void testForkGet() {
+        testForkGet(mainPool());
+    }
+    public void testForkGet_Singleton() {
+        testForkGet(singletonPool());
+    }
+    public void testForkGet(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertNull(f.get());
+                f.checkCompletedNormally();
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * timed get of a forked task returns when task completes
+     */
+    public void testForkTimedGet() {
+        testForkTimedGet(mainPool());
+    }
+    public void testForkTimedGet_Singleton() {
+        testForkTimedGet(singletonPool());
+    }
+    public void testForkTimedGet(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertNull(f.get(LONG_DELAY_MS, MILLISECONDS));
+                f.checkCompletedNormally();
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * timed get with null time unit throws NullPointerException
+     */
+    public void testForkTimedGetNullTimeUnit() {
+        testForkTimedGetNullTimeUnit(mainPool());
+    }
+    public void testForkTimedGetNullTimeUnit_Singleton() {
+        testForkTimedGet(singletonPool());
+    }
+    public void testForkTimedGetNullTimeUnit(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, null);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes
+     */
+    public void testForkQuietlyJoin() {
+        testForkQuietlyJoin(mainPool());
+    }
+    public void testForkQuietlyJoin_Singleton() {
+        testForkQuietlyJoin(singletonPool());
+    }
+    public void testForkQuietlyJoin(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                f.checkCompletedNormally();
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * helpQuiesce returns when tasks are complete.
+     * getQueuedTaskCount returns 0 when quiescent
+     */
+    public void testForkHelpQuiesce() {
+        testForkHelpQuiesce(mainPool());
+    }
+    public void testForkHelpQuiesce_Singleton() {
+        testForkHelpQuiesce(singletonPool());
+    }
+    public void testForkHelpQuiesce(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                helpQuiesce();
+                assertEquals(0, getQueuedTaskCount());
+                f.checkCompletedNormally();
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * invoke task throws exception when task completes abnormally
+     */
+    public void testAbnormalInvoke() {
+        testAbnormalInvoke(mainPool());
+    }
+    public void testAbnormalInvoke_Singleton() {
+        testAbnormalInvoke(singletonPool());
+    }
+    public void testAbnormalInvoke(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes abnormally
+     */
+    public void testAbnormalQuietlyInvoke() {
+        testAbnormalQuietlyInvoke(mainPool());
+    }
+    public void testAbnormalQuietlyInvoke_Singleton() {
+        testAbnormalQuietlyInvoke(singletonPool());
+    }
+    public void testAbnormalQuietlyInvoke(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                f.quietlyInvoke();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * join of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkJoin() {
+        testAbnormalForkJoin(mainPool());
+    }
+    public void testAbnormalForkJoin_Singleton() {
+        testAbnormalForkJoin(singletonPool());
+    }
+    public void testAbnormalForkJoin(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkGet() {
+        testAbnormalForkGet(mainPool());
+    }
+    public void testAbnormalForkGet_Singleton() {
+        testAbnormalForkJoin(singletonPool());
+    }
+    public void testAbnormalForkGet(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkTimedGet() {
+        testAbnormalForkTimedGet(mainPool());
+    }
+    public void testAbnormalForkTimedGet_Singleton() {
+        testAbnormalForkTimedGet(singletonPool());
+    }
+    public void testAbnormalForkTimedGet(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes abnormally
+     */
+    public void testAbnormalForkQuietlyJoin() {
+        testAbnormalForkQuietlyJoin(mainPool());
+    }
+    public void testAbnormalForkQuietlyJoin_Singleton() {
+        testAbnormalForkQuietlyJoin(singletonPool());
+    }
+    public void testAbnormalForkQuietlyJoin(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * getPool of executing task returns its pool
+     */
+    public void testGetPool() {
+        testGetPool(mainPool());
+    }
+    public void testGetPool_Singleton() {
+        testGetPool(singletonPool());
+    }
+    public void testGetPool(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertSame(pool, getPool());
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * getPool of non-FJ task returns null
+     */
+    public void testGetPool2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertNull(getPool());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * inForkJoinPool of executing task returns true
+     */
+    public void testInForkJoinPool() {
+        testInForkJoinPool(mainPool());
+    }
+    public void testInForkJoinPool_Singleton() {
+        testInForkJoinPool(singletonPool());
+    }
+    public void testInForkJoinPool(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertTrue(inForkJoinPool());
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * inForkJoinPool of non-FJ task returns false
+     */
+    public void testInForkJoinPool2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertFalse(inForkJoinPool());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * setRawResult(null) succeeds
+     */
+    public void testSetRawResult() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                setRawResult(null);
+                assertNull(getRawResult());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * invoke task throws exception after invoking completeExceptionally
+     */
+    public void testCompleteExceptionally() {
+        testCompleteExceptionally(mainPool());
+    }
+    public void testCompleteExceptionally_Singleton() {
+        testCompleteExceptionally(singletonPool());
+    }
+    public void testCompleteExceptionally(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                f.completeExceptionally(new FJException());
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument invokes task
+     */
+    public void testInvokeAll1() {
+        testInvokeAll1(mainPool());
+    }
+    public void testInvokeAll1_Singleton() {
+        testInvokeAll1(singletonPool());
+    }
+    public void testInvokeAll1(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                invokeAll(f);
+                f.checkCompletedNormally();
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * invokeAll(t1, t2) invokes all task arguments
+     */
+    public void testInvokeAll2() {
+        testInvokeAll2(mainPool());
+    }
+    public void testInvokeAll2_Singleton() {
+        testInvokeAll2(singletonPool());
+    }
+    public void testInvokeAll2(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib[] tasks = {
+                    new AsyncFib(8),
+                    new AsyncFib(9),
+                };
+                invokeAll(tasks[0], tasks[1]);
+                for (AsyncFib task : tasks) assertTrue(task.isDone());
+                for (AsyncFib task : tasks) task.checkCompletedNormally();
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument invokes tasks
+     */
+    public void testInvokeAll3() {
+        testInvokeAll3(mainPool());
+    }
+    public void testInvokeAll3_Singleton() {
+        testInvokeAll3(singletonPool());
+    }
+    public void testInvokeAll3(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib[] tasks = {
+                    new AsyncFib(8),
+                    new AsyncFib(9),
+                    new AsyncFib(7),
+                };
+                invokeAll(tasks[0], tasks[1], tasks[2]);
+                for (AsyncFib task : tasks) assertTrue(task.isDone());
+                for (AsyncFib task : tasks) task.checkCompletedNormally();
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * invokeAll(collection) invokes all tasks in the collection
+     */
+    public void testInvokeAllCollection() {
+        testInvokeAllCollection(mainPool());
+    }
+    public void testInvokeAllCollection_Singleton() {
+        testInvokeAllCollection(singletonPool());
+    }
+    public void testInvokeAllCollection(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib[] tasks = {
+                    new AsyncFib(8),
+                    new AsyncFib(9),
+                    new AsyncFib(7),
+                };
+                invokeAll(Arrays.asList(tasks));
+                for (AsyncFib task : tasks) assertTrue(task.isDone());
+                for (AsyncFib task : tasks) task.checkCompletedNormally();
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * invokeAll(tasks) with any null task throws NullPointerException
+     */
+    public void testInvokeAllNullTask() {
+        testInvokeAllNullTask(mainPool());
+    }
+    public void testInvokeAllNullTask_Singleton() {
+        testInvokeAllNullTask(singletonPool());
+    }
+    public void testInvokeAllNullTask(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib nul = null;
+                Runnable[] throwingActions = {
+                    () -> invokeAll(nul),
+                    () -> invokeAll(nul, nul),
+                    () -> invokeAll(new AsyncFib(8), new AsyncFib(9), nul),
+                    () -> invokeAll(new AsyncFib(8), nul, new AsyncFib(9)),
+                    () -> invokeAll(nul, new AsyncFib(8), new AsyncFib(9)),
+                };
+                assertThrows(NullPointerException.class, throwingActions);
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument throws exception if task does
+     */
+    public void testAbnormalInvokeAll1() {
+        testAbnormalInvokeAll1(mainPool());
+    }
+    public void testAbnormalInvokeAll1_Singleton() {
+        testAbnormalInvokeAll1(singletonPool());
+    }
+    public void testAbnormalInvokeAll1(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib g = new FailingAsyncFib(9);
+                try {
+                    invokeAll(g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * invokeAll(t1, t2) throw exception if any task does
+     */
+    public void testAbnormalInvokeAll2() {
+        testAbnormalInvokeAll2(mainPool());
+    }
+    public void testAbnormalInvokeAll2_Singleton() {
+        testAbnormalInvokeAll2(singletonPool());
+    }
+    public void testAbnormalInvokeAll2(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                FailingAsyncFib g = new FailingAsyncFib(9);
+                ForkJoinTask[] tasks = { f, g };
+                Collections.shuffle(Arrays.asList(tasks));
+                try {
+                    invokeAll(tasks[0], tasks[1]);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument throws exception if any task does
+     */
+    public void testAbnormalInvokeAll3() {
+        testAbnormalInvokeAll3(mainPool());
+    }
+    public void testAbnormalInvokeAll3_Singleton() {
+        testAbnormalInvokeAll3(singletonPool());
+    }
+    public void testAbnormalInvokeAll3(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                FailingAsyncFib g = new FailingAsyncFib(9);
+                AsyncFib h = new AsyncFib(7);
+                ForkJoinTask[] tasks = { f, g, h };
+                Collections.shuffle(Arrays.asList(tasks));
+                try {
+                    invokeAll(tasks[0], tasks[1], tasks[2]);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * invokeAll(collection) throws exception if any task does
+     */
+    public void testAbnormalInvokeAllCollection() {
+        testAbnormalInvokeAllCollection(mainPool());
+    }
+    public void testAbnormalInvokeAllCollection_Singleton() {
+        testAbnormalInvokeAllCollection(singletonPool());
+    }
+    public void testAbnormalInvokeAllCollection(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                AsyncFib h = new AsyncFib(7);
+                ForkJoinTask[] tasks = { f, g, h };
+                Collections.shuffle(Arrays.asList(tasks));
+                try {
+                    invokeAll(Arrays.asList(tasks));
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * tryUnfork returns true for most recent unexecuted task,
+     * and suppresses execution
+     */
+    public void testTryUnfork() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertTrue(f.tryUnfork());
+                helpQuiesce();
+                checkNotDone(f);
+                g.checkCompletedNormally();
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * getSurplusQueuedTaskCount returns > 0 when
+     * there are more tasks than threads
+     */
+    public void testGetSurplusQueuedTaskCount() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib h = new AsyncFib(7);
+                assertSame(h, h.fork());
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertTrue(getSurplusQueuedTaskCount() > 0);
+                helpQuiesce();
+                assertEquals(0, getSurplusQueuedTaskCount());
+                f.checkCompletedNormally();
+                g.checkCompletedNormally();
+                h.checkCompletedNormally();
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * peekNextLocalTask returns most recent unexecuted task.
+     */
+    public void testPeekNextLocalTask() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(f, peekNextLocalTask());
+                assertNull(f.join());
+                f.checkCompletedNormally();
+                helpQuiesce();
+                g.checkCompletedNormally();
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * pollNextLocalTask returns most recent unexecuted task without
+     * executing it
+     */
+    public void testPollNextLocalTask() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(f, pollNextLocalTask());
+                helpQuiesce();
+                checkNotDone(f);
+                g.checkCompletedNormally();
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * pollTask returns an unexecuted task without executing it
+     */
+    public void testPollTask() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(f, pollTask());
+                helpQuiesce();
+                checkNotDone(f);
+                g.checkCompletedNormally();
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * peekNextLocalTask returns least recent unexecuted task in async mode
+     */
+    public void testPeekNextLocalTaskAsync() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(g, peekNextLocalTask());
+                assertNull(f.join());
+                helpQuiesce();
+                f.checkCompletedNormally();
+                g.checkCompletedNormally();
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    /**
+     * pollNextLocalTask returns least recent unexecuted task without
+     * executing it, in async mode
+     */
+    public void testPollNextLocalTaskAsync() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(g, pollNextLocalTask());
+                helpQuiesce();
+                f.checkCompletedNormally();
+                checkNotDone(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    /**
+     * pollTask returns an unexecuted task without executing it, in
+     * async mode
+     */
+    public void testPollTaskAsync() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(g, pollTask());
+                helpQuiesce();
+                f.checkCompletedNormally();
+                checkNotDone(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    /**
+     * ForkJoinTask.quietlyComplete returns when task completes
+     * normally without setting a value. The most recent value
+     * established by setRawResult(V) (or null by default) is returned
+     * from invoke.
+     */
+    public void testQuietlyComplete() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+                protected void realCompute() {
+                    AsyncFib f = new AsyncFib(8);
+                    f.quietlyComplete();
+                    assertEquals(8, f.number);
+                    assertTrue(f.isDone());
+                    assertFalse(f.isCancelled());
+                    assertTrue(f.isCompletedNormally());
+                    assertFalse(f.isCompletedAbnormally());
+                    assertNull(f.getException());
+                }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    // jdk9
+
+    /**
+     * pollSubmission returns unexecuted submitted task, if present
+     */
+    public void testPollSubmission() {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ForkJoinTask a = ForkJoinTask.adapt(awaiter(done));
+        final ForkJoinTask b = ForkJoinTask.adapt(awaiter(done));
+        final ForkJoinTask c = ForkJoinTask.adapt(awaiter(done));
+        final ForkJoinPool p = singletonPool();
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            Thread external = new Thread(new CheckedRunnable() {
+                public void realRun() {
+                    p.execute(a);
+                    p.execute(b);
+                    p.execute(c);
+                }});
+            RecursiveAction s = new CheckedRecursiveAction() {
+                protected void realCompute() {
+                    external.start();
+                    try {
+                        external.join();
+                    } catch (Exception ex) {
+                        threadUnexpectedException(ex);
+                    }
+                    assertTrue(p.hasQueuedSubmissions());
+                    assertTrue(Thread.currentThread() instanceof ForkJoinWorkerThread);
+                    ForkJoinTask r = ForkJoinTask.pollSubmission();
+                    assertTrue(r == a || r == b || r == c);
+                    assertFalse(r.isDone());
+                }};
+            p.invoke(s);
+        }
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/ForkJoinTaskTest.java b/jdk/test/java/util/concurrent/tck/ForkJoinTaskTest.java
new file mode 100644
index 00000000000..7000c1a4bb5
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/ForkJoinTaskTest.java
@@ -0,0 +1,1685 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.RecursiveAction;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ForkJoinTaskTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(ForkJoinTaskTest.class);
+    }
+
+    // Runs with "mainPool" use > 1 thread. singletonPool tests use 1
+    static final int mainPoolSize =
+        Math.max(2, Runtime.getRuntime().availableProcessors());
+
+    private static ForkJoinPool mainPool() {
+        return new ForkJoinPool(mainPoolSize);
+    }
+
+    private static ForkJoinPool singletonPool() {
+        return new ForkJoinPool(1);
+    }
+
+    private static ForkJoinPool asyncSingletonPool() {
+        return new ForkJoinPool(1,
+                                ForkJoinPool.defaultForkJoinWorkerThreadFactory,
+                                null, true);
+    }
+
+    private void testInvokeOnPool(ForkJoinPool pool, RecursiveAction a) {
+        try (PoolCleaner cleaner = cleaner(pool)) {
+            assertFalse(a.isDone());
+            assertFalse(a.isCompletedNormally());
+            assertFalse(a.isCompletedAbnormally());
+            assertFalse(a.isCancelled());
+            assertNull(a.getException());
+            assertNull(a.getRawResult());
+
+            assertNull(pool.invoke(a));
+
+            assertTrue(a.isDone());
+            assertTrue(a.isCompletedNormally());
+            assertFalse(a.isCompletedAbnormally());
+            assertFalse(a.isCancelled());
+            assertNull(a.getException());
+            assertNull(a.getRawResult());
+        }
+    }
+
+    void checkNotDone(ForkJoinTask a) {
+        assertFalse(a.isDone());
+        assertFalse(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertFalse(a.isCancelled());
+        assertNull(a.getException());
+        assertNull(a.getRawResult());
+
+        try {
+            a.get(0L, SECONDS);
+            shouldThrow();
+        } catch (TimeoutException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    <T> void checkCompletedNormally(ForkJoinTask<T> a) {
+        checkCompletedNormally(a, null);
+    }
+
+    <T> void checkCompletedNormally(ForkJoinTask<T> a, T expected) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertTrue(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertNull(a.getException());
+        assertSame(expected, a.getRawResult());
+
+        {
+            Thread.currentThread().interrupt();
+            long startTime = System.nanoTime();
+            assertSame(expected, a.join());
+            assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
+            Thread.interrupted();
+        }
+
+        {
+            Thread.currentThread().interrupt();
+            long startTime = System.nanoTime();
+            a.quietlyJoin();        // should be no-op
+            assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
+            Thread.interrupted();
+        }
+
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+        try {
+            assertSame(expected, a.get());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        try {
+            assertSame(expected, a.get(5L, SECONDS));
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCancelled(ForkJoinTask a) {
+        assertTrue(a.isDone());
+        assertTrue(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertTrue(a.getException() instanceof CancellationException);
+        assertNull(a.getRawResult());
+        assertTrue(a.cancel(false));
+        assertTrue(a.cancel(true));
+
+        try {
+            Thread.currentThread().interrupt();
+            a.join();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        Thread.interrupted();
+
+        {
+            long startTime = System.nanoTime();
+            a.quietlyJoin();        // should be no-op
+            assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
+        }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCompletedAbnormally(ForkJoinTask a, Throwable t) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertSame(t.getClass(), a.getException().getClass());
+        assertNull(a.getRawResult());
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+
+        try {
+            Thread.currentThread().interrupt();
+            a.join();
+            shouldThrow();
+        } catch (Throwable expected) {
+            assertSame(t.getClass(), expected.getClass());
+        }
+        Thread.interrupted();
+
+        {
+            long startTime = System.nanoTime();
+            a.quietlyJoin();        // should be no-op
+            assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
+        }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    /*
+     * Testing coverage notes:
+     *
+     * To test extension methods and overrides, most tests use
+     * BinaryAsyncAction extension class that processes joins
+     * differently than supplied Recursive forms.
+     */
+
+    public static final class FJException extends RuntimeException {
+        FJException() { super(); }
+    }
+
+    abstract static class BinaryAsyncAction extends ForkJoinTask<Void> {
+        private volatile int controlState;
+
+        static final AtomicIntegerFieldUpdater<BinaryAsyncAction> controlStateUpdater =
+            AtomicIntegerFieldUpdater.newUpdater(BinaryAsyncAction.class,
+                                                 "controlState");
+
+        private volatile BinaryAsyncAction parent;
+
+        private volatile BinaryAsyncAction sibling;
+
+        protected BinaryAsyncAction() {
+        }
+
+        public final Void getRawResult() { return null; }
+        protected final void setRawResult(Void mustBeNull) { }
+
+        public final void linkSubtasks(BinaryAsyncAction x, BinaryAsyncAction y) {
+            x.parent = y.parent = this;
+            x.sibling = y;
+            y.sibling = x;
+        }
+
+        protected void onComplete(BinaryAsyncAction x, BinaryAsyncAction y) {
+        }
+
+        protected boolean onException() {
+            return true;
+        }
+
+        public void linkAndForkSubtasks(BinaryAsyncAction x, BinaryAsyncAction y) {
+            linkSubtasks(x, y);
+            y.fork();
+            x.fork();
+        }
+
+        private void completeThis() {
+            super.complete(null);
+        }
+
+        private void completeThisExceptionally(Throwable ex) {
+            super.completeExceptionally(ex);
+        }
+
+        public boolean cancel(boolean mayInterruptIfRunning) {
+            if (super.cancel(mayInterruptIfRunning)) {
+                completeExceptionally(new FJException());
+                return true;
+            }
+            return false;
+        }
+
+        public final void complete() {
+            BinaryAsyncAction a = this;
+            for (;;) {
+                BinaryAsyncAction s = a.sibling;
+                BinaryAsyncAction p = a.parent;
+                a.sibling = null;
+                a.parent = null;
+                a.completeThis();
+                if (p == null || p.compareAndSetControlState(0, 1))
+                    break;
+                try {
+                    p.onComplete(a, s);
+                } catch (Throwable rex) {
+                    p.completeExceptionally(rex);
+                    return;
+                }
+                a = p;
+            }
+        }
+
+        public final void completeExceptionally(Throwable ex) {
+            for (BinaryAsyncAction a = this;;) {
+                a.completeThisExceptionally(ex);
+                BinaryAsyncAction s = a.sibling;
+                if (s != null && !s.isDone())
+                    s.completeExceptionally(ex);
+                if ((a = a.parent) == null)
+                    break;
+            }
+        }
+
+        public final BinaryAsyncAction getParent() {
+            return parent;
+        }
+
+        public BinaryAsyncAction getSibling() {
+            return sibling;
+        }
+
+        public void reinitialize() {
+            parent = sibling = null;
+            super.reinitialize();
+        }
+
+        protected final int getControlState() {
+            return controlState;
+        }
+
+        protected final boolean compareAndSetControlState(int expect,
+                                                          int update) {
+            return controlStateUpdater.compareAndSet(this, expect, update);
+        }
+
+        protected final void setControlState(int value) {
+            controlState = value;
+        }
+
+        protected final void incrementControlState() {
+            controlStateUpdater.incrementAndGet(this);
+        }
+
+        protected final void decrementControlState() {
+            controlStateUpdater.decrementAndGet(this);
+        }
+
+    }
+
+    static final class AsyncFib extends BinaryAsyncAction {
+        int number;
+        public AsyncFib(int n) {
+            this.number = n;
+        }
+
+        public final boolean exec() {
+            AsyncFib f = this;
+            int n = f.number;
+            while (n > 1) {
+                AsyncFib p = f;
+                AsyncFib r = new AsyncFib(n - 2);
+                f = new AsyncFib(--n);
+                p.linkSubtasks(r, f);
+                r.fork();
+            }
+            f.complete();
+            return false;
+        }
+
+        protected void onComplete(BinaryAsyncAction x, BinaryAsyncAction y) {
+            number = ((AsyncFib)x).number + ((AsyncFib)y).number;
+        }
+    }
+
+    static final class FailingAsyncFib extends BinaryAsyncAction {
+        int number;
+        public FailingAsyncFib(int n) {
+            this.number = n;
+        }
+
+        public final boolean exec() {
+            FailingAsyncFib f = this;
+            int n = f.number;
+            while (n > 1) {
+                FailingAsyncFib p = f;
+                FailingAsyncFib r = new FailingAsyncFib(n - 2);
+                f = new FailingAsyncFib(--n);
+                p.linkSubtasks(r, f);
+                r.fork();
+            }
+            f.complete();
+            return false;
+        }
+
+        protected void onComplete(BinaryAsyncAction x, BinaryAsyncAction y) {
+            completeExceptionally(new FJException());
+        }
+    }
+
+    /**
+     * invoke returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks; getRawResult returns null.
+     */
+    public void testInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertNull(f.invoke());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks
+     */
+    public void testQuietlyInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                f.quietlyInvoke();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join of a forked task returns when task completes
+     */
+    public void testForkJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertNull(f.join());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * get of a forked task returns when task completes
+     */
+    public void testForkGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertNull(f.get());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get of a forked task returns when task completes
+     */
+    public void testForkTimedGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertNull(f.get(LONG_DELAY_MS, MILLISECONDS));
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get with null time unit throws NPE
+     */
+    public void testForkTimedGetNPE() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, null);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes
+     */
+    public void testForkQuietlyJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * helpQuiesce returns when tasks are complete.
+     * getQueuedTaskCount returns 0 when quiescent
+     */
+    public void testForkHelpQuiesce() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                helpQuiesce();
+                assertEquals(21, f.number);
+                assertEquals(0, getQueuedTaskCount());
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task completes abnormally
+     */
+    public void testAbnormalInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes abnormally
+     */
+    public void testAbnormalQuietlyInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                f.quietlyInvoke();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkTimedGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes abnormally
+     */
+    public void testAbnormalForkQuietlyJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task cancelled
+     */
+    public void testCancelledInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkTimedGet() throws Exception {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task cancelled
+     */
+    public void testCancelledForkQuietlyJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                checkCancelled(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * getPool of executing task returns its pool
+     */
+    public void testGetPool() {
+        final ForkJoinPool mainPool = mainPool();
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertSame(mainPool, getPool());
+            }};
+        testInvokeOnPool(mainPool, a);
+    }
+
+    /**
+     * getPool of non-FJ task returns null
+     */
+    public void testGetPool2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertNull(getPool());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * inForkJoinPool of executing task returns true
+     */
+    public void testInForkJoinPool() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertTrue(inForkJoinPool());
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * inForkJoinPool of non-FJ task returns false
+     */
+    public void testInForkJoinPool2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertFalse(inForkJoinPool());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * setRawResult(null) succeeds
+     */
+    public void testSetRawResult() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                setRawResult(null);
+                assertNull(getRawResult());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * invoke task throws exception after invoking completeExceptionally
+     */
+    public void testCompleteExceptionally() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                f.completeExceptionally(new FJException());
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * completeExceptionally(null) surprisingly has the same effect as
+     * completeExceptionally(new RuntimeException())
+     */
+    public void testCompleteExceptionally_null() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                f.completeExceptionally(null);
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (RuntimeException success) {
+                    assertSame(success.getClass(), RuntimeException.class);
+                    assertNull(success.getCause());
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) invokes all task arguments
+     */
+    public void testInvokeAll2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                invokeAll(f, g);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument invokes task
+     */
+    public void testInvokeAll1() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                invokeAll(f);
+                checkCompletedNormally(f);
+                assertEquals(21, f.number);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument invokes tasks
+     */
+    public void testInvokeAll3() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                AsyncFib h = new AsyncFib(7);
+                invokeAll(f, g, h);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                assertEquals(13, h.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(collection) invokes all tasks in the collection
+     */
+    public void testInvokeAllCollection() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                AsyncFib h = new AsyncFib(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                invokeAll(set);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                assertEquals(13, h.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with any null task throws NPE
+     */
+    public void testInvokeAllNPE() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                AsyncFib h = null;
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) throw exception if any task does
+     */
+    public void testAbnormalInvokeAll2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                FailingAsyncFib g = new FailingAsyncFib(9);
+                ForkJoinTask[] tasks = { f, g };
+                Collections.shuffle(Arrays.asList(tasks));
+                try {
+                    invokeAll(tasks);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument throws exception if task does
+     */
+    public void testAbnormalInvokeAll1() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib g = new FailingAsyncFib(9);
+                try {
+                    invokeAll(g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument throws exception if any task does
+     */
+    public void testAbnormalInvokeAll3() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                FailingAsyncFib g = new FailingAsyncFib(9);
+                AsyncFib h = new AsyncFib(7);
+                ForkJoinTask[] tasks = { f, g, h };
+                Collections.shuffle(Arrays.asList(tasks));
+                try {
+                    invokeAll(tasks);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(collection) throws exception if any task does
+     */
+    public void testAbnormalInvokeAllCollection() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                AsyncFib h = new AsyncFib(7);
+                ForkJoinTask[] tasks = { f, g, h };
+                List taskList = Arrays.asList(tasks);
+                Collections.shuffle(taskList);
+                try {
+                    invokeAll(taskList);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * tryUnfork returns true for most recent unexecuted task,
+     * and suppresses execution
+     */
+    public void testTryUnfork() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertTrue(f.tryUnfork());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * getSurplusQueuedTaskCount returns > 0 when
+     * there are more tasks than threads
+     */
+    public void testGetSurplusQueuedTaskCount() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib h = new AsyncFib(7);
+                assertSame(h, h.fork());
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertTrue(getSurplusQueuedTaskCount() > 0);
+                helpQuiesce();
+                assertEquals(0, getSurplusQueuedTaskCount());
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * peekNextLocalTask returns most recent unexecuted task.
+     */
+    public void testPeekNextLocalTask() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(f, peekNextLocalTask());
+                assertNull(f.join());
+                checkCompletedNormally(f);
+                helpQuiesce();
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * pollNextLocalTask returns most recent unexecuted task without
+     * executing it
+     */
+    public void testPollNextLocalTask() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(f, pollNextLocalTask());
+                helpQuiesce();
+                checkNotDone(f);
+                assertEquals(34, g.number);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * pollTask returns an unexecuted task without executing it
+     */
+    public void testPollTask() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(f, pollTask());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * peekNextLocalTask returns least recent unexecuted task in async mode
+     */
+    public void testPeekNextLocalTaskAsync() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(g, peekNextLocalTask());
+                assertNull(f.join());
+                helpQuiesce();
+                checkCompletedNormally(f);
+                assertEquals(34, g.number);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    /**
+     * pollNextLocalTask returns least recent unexecuted task without
+     * executing it, in async mode
+     */
+    public void testPollNextLocalTaskAsync() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(g, pollNextLocalTask());
+                helpQuiesce();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+                checkNotDone(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    /**
+     * pollTask returns an unexecuted task without executing it, in
+     * async mode
+     */
+    public void testPollTaskAsync() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(g, pollTask());
+                helpQuiesce();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+                checkNotDone(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    // versions for singleton pools
+
+    /**
+     * invoke returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks; getRawResult returns null.
+     */
+    public void testInvokeSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertNull(f.invoke());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks
+     */
+    public void testQuietlyInvokeSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                f.quietlyInvoke();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * join of a forked task returns when task completes
+     */
+    public void testForkJoinSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertNull(f.join());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * get of a forked task returns when task completes
+     */
+    public void testForkGetSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertNull(f.get());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * timed get of a forked task returns when task completes
+     */
+    public void testForkTimedGetSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertNull(f.get(LONG_DELAY_MS, MILLISECONDS));
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * timed get with null time unit throws NPE
+     */
+    public void testForkTimedGetNPESingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, null);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes
+     */
+    public void testForkQuietlyJoinSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * helpQuiesce returns when tasks are complete.
+     * getQueuedTaskCount returns 0 when quiescent
+     */
+    public void testForkHelpQuiesceSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                helpQuiesce();
+                assertEquals(0, getQueuedTaskCount());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task completes abnormally
+     */
+    public void testAbnormalInvokeSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes abnormally
+     */
+    public void testAbnormalQuietlyInvokeSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                f.quietlyInvoke();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkJoinSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkGetSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkTimedGetSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes abnormally
+     */
+    public void testAbnormalForkQuietlyJoinSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task cancelled
+     */
+    public void testCancelledInvokeSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkJoinSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkGetSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkTimedGetSingleton() throws Exception {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task cancelled
+     */
+    public void testCancelledForkQuietlyJoinSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                checkCancelled(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invoke task throws exception after invoking completeExceptionally
+     */
+    public void testCompleteExceptionallySingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                f.completeExceptionally(new FJException());
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) invokes all task arguments
+     */
+    public void testInvokeAll2Singleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                invokeAll(f, g);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument invokes task
+     */
+    public void testInvokeAll1Singleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                invokeAll(f);
+                checkCompletedNormally(f);
+                assertEquals(21, f.number);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument invokes tasks
+     */
+    public void testInvokeAll3Singleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                AsyncFib h = new AsyncFib(7);
+                invokeAll(f, g, h);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                assertEquals(13, h.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(collection) invokes all tasks in the collection
+     */
+    public void testInvokeAllCollectionSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                AsyncFib h = new AsyncFib(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                invokeAll(set);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                assertEquals(13, h.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with any null task throws NPE
+     */
+    public void testInvokeAllNPESingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                AsyncFib h = null;
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) throw exception if any task does
+     */
+    public void testAbnormalInvokeAll2Singleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                FailingAsyncFib g = new FailingAsyncFib(9);
+                ForkJoinTask[] tasks = { f, g };
+                Collections.shuffle(Arrays.asList(tasks));
+                try {
+                    invokeAll(tasks);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument throws exception if task does
+     */
+    public void testAbnormalInvokeAll1Singleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib g = new FailingAsyncFib(9);
+                try {
+                    invokeAll(g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument throws exception if any task does
+     */
+    public void testAbnormalInvokeAll3Singleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                FailingAsyncFib g = new FailingAsyncFib(9);
+                AsyncFib h = new AsyncFib(7);
+                ForkJoinTask[] tasks = { f, g, h };
+                Collections.shuffle(Arrays.asList(tasks));
+                try {
+                    invokeAll(tasks);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(collection) throws exception if any task does
+     */
+    public void testAbnormalInvokeAllCollectionSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                AsyncFib h = new AsyncFib(7);
+                ForkJoinTask[] tasks = { f, g, h };
+                List taskList = Arrays.asList(tasks);
+                Collections.shuffle(taskList);
+                try {
+                    invokeAll(taskList);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * ForkJoinTask.quietlyComplete returns when task completes
+     * normally without setting a value. The most recent value
+     * established by setRawResult(V) (or null by default) is returned
+     * from invoke.
+     */
+    public void testQuietlyComplete() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+                protected void realCompute() {
+                    AsyncFib f = new AsyncFib(8);
+                    f.quietlyComplete();
+                    assertEquals(8, f.number);
+                    checkCompletedNormally(f);
+                }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/FutureTaskTest.java b/jdk/test/java/util/concurrent/tck/FutureTaskTest.java
new file mode 100644
index 00000000000..1e858bfdbc1
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/FutureTaskTest.java
@@ -0,0 +1,866 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class FutureTaskTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(FutureTaskTest.class);
+    }
+
+    void checkIsDone(Future<?> f) {
+        assertTrue(f.isDone());
+        assertFalse(f.cancel(false));
+        assertFalse(f.cancel(true));
+        if (f instanceof PublicFutureTask) {
+            PublicFutureTask pf = (PublicFutureTask) f;
+            assertEquals(1, pf.doneCount());
+            assertFalse(pf.runAndReset());
+            assertEquals(1, pf.doneCount());
+            Object r = null; Object exInfo = null;
+            try {
+                r = f.get();
+            } catch (CancellationException t) {
+                exInfo = CancellationException.class;
+            } catch (ExecutionException t) {
+                exInfo = t.getCause();
+            } catch (Throwable t) {
+                threadUnexpectedException(t);
+            }
+
+            // Check that run and runAndReset have no effect.
+            int savedRunCount = pf.runCount();
+            pf.run();
+            pf.runAndReset();
+            assertEquals(savedRunCount, pf.runCount());
+            try {
+                assertSame(r, f.get());
+            } catch (CancellationException t) {
+                assertSame(exInfo, CancellationException.class);
+            } catch (ExecutionException t) {
+                assertSame(exInfo, t.getCause());
+            } catch (Throwable t) {
+                threadUnexpectedException(t);
+            }
+            assertTrue(f.isDone());
+        }
+    }
+
+    void checkNotDone(Future<?> f) {
+        assertFalse(f.isDone());
+        assertFalse(f.isCancelled());
+        if (f instanceof PublicFutureTask) {
+            PublicFutureTask pf = (PublicFutureTask) f;
+            assertEquals(0, pf.doneCount());
+            assertEquals(0, pf.setCount());
+            assertEquals(0, pf.setExceptionCount());
+        }
+    }
+
+    void checkIsRunning(Future<?> f) {
+        checkNotDone(f);
+        if (f instanceof FutureTask) {
+            FutureTask ft = (FutureTask<?>) f;
+            // Check that run methods do nothing
+            ft.run();
+            if (f instanceof PublicFutureTask) {
+                PublicFutureTask pf = (PublicFutureTask) f;
+                int savedRunCount = pf.runCount();
+                pf.run();
+                assertFalse(pf.runAndReset());
+                assertEquals(savedRunCount, pf.runCount());
+            }
+            checkNotDone(f);
+        }
+    }
+
+    <T> void checkCompletedNormally(Future<T> f, T expected) {
+        checkIsDone(f);
+        assertFalse(f.isCancelled());
+
+        try {
+            assertSame(expected, f.get());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        try {
+            assertSame(expected, f.get(5L, SECONDS));
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCancelled(Future<?> f) {
+        checkIsDone(f);
+        assertTrue(f.isCancelled());
+
+        try {
+            f.get();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            f.get(5L, SECONDS);
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void tryToConfuseDoneTask(PublicFutureTask pf) {
+        pf.set(new Object());
+        pf.setException(new Error());
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false }) {
+            pf.cancel(mayInterruptIfRunning);
+        }
+    }
+
+    void checkCompletedAbnormally(Future<?> f, Throwable t) {
+        checkIsDone(f);
+        assertFalse(f.isCancelled());
+
+        try {
+            f.get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t, success.getCause());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            f.get(5L, SECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t, success.getCause());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    /**
+     * Subclass to expose protected methods
+     */
+    static class PublicFutureTask extends FutureTask {
+        private final AtomicInteger runCount;
+        private final AtomicInteger doneCount = new AtomicInteger(0);
+        private final AtomicInteger runAndResetCount = new AtomicInteger(0);
+        private final AtomicInteger setCount = new AtomicInteger(0);
+        private final AtomicInteger setExceptionCount = new AtomicInteger(0);
+        public int runCount() { return runCount.get(); }
+        public int doneCount() { return doneCount.get(); }
+        public int runAndResetCount() { return runAndResetCount.get(); }
+        public int setCount() { return setCount.get(); }
+        public int setExceptionCount() { return setExceptionCount.get(); }
+
+        PublicFutureTask(Runnable runnable) {
+            this(runnable, seven);
+        }
+        PublicFutureTask(Runnable runnable, Object result) {
+            this(runnable, result, new AtomicInteger(0));
+        }
+        private PublicFutureTask(final Runnable runnable, Object result,
+                                 final AtomicInteger runCount) {
+            super(new Runnable() {
+                public void run() {
+                    runCount.getAndIncrement();
+                    runnable.run();
+                }}, result);
+            this.runCount = runCount;
+        }
+        PublicFutureTask(Callable callable) {
+            this(callable, new AtomicInteger(0));
+        }
+        private PublicFutureTask(final Callable callable,
+                                 final AtomicInteger runCount) {
+            super(new Callable() {
+                public Object call() throws Exception {
+                    runCount.getAndIncrement();
+                    return callable.call();
+                }});
+            this.runCount = runCount;
+        }
+        @Override public void done() {
+            assertTrue(isDone());
+            doneCount.incrementAndGet();
+            super.done();
+        }
+        @Override public boolean runAndReset() {
+            runAndResetCount.incrementAndGet();
+            return super.runAndReset();
+        }
+        @Override public void set(Object x) {
+            setCount.incrementAndGet();
+            super.set(x);
+        }
+        @Override public void setException(Throwable t) {
+            setExceptionCount.incrementAndGet();
+            super.setException(t);
+        }
+    }
+
+    class Counter extends CheckedRunnable {
+        final AtomicInteger count = new AtomicInteger(0);
+        public int get() { return count.get(); }
+        public void realRun() {
+            count.getAndIncrement();
+        }
+    }
+
+    /**
+     * creating a future with a null callable throws NullPointerException
+     */
+    public void testConstructor() {
+        try {
+            new FutureTask(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * creating a future with null runnable throws NullPointerException
+     */
+    public void testConstructor2() {
+        try {
+            new FutureTask(null, Boolean.TRUE);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * isDone is true when a task completes
+     */
+    public void testIsDone() {
+        PublicFutureTask task = new PublicFutureTask(new NoOpCallable());
+        assertFalse(task.isDone());
+        task.run();
+        assertTrue(task.isDone());
+        checkCompletedNormally(task, Boolean.TRUE);
+        assertEquals(1, task.runCount());
+    }
+
+    /**
+     * runAndReset of a non-cancelled task succeeds
+     */
+    public void testRunAndReset() {
+        PublicFutureTask task = new PublicFutureTask(new NoOpCallable());
+        for (int i = 0; i < 3; i++) {
+            assertTrue(task.runAndReset());
+            checkNotDone(task);
+            assertEquals(i + 1, task.runCount());
+            assertEquals(i + 1, task.runAndResetCount());
+            assertEquals(0, task.setCount());
+            assertEquals(0, task.setExceptionCount());
+        }
+    }
+
+    /**
+     * runAndReset after cancellation fails
+     */
+    public void testRunAndResetAfterCancel() {
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false }) {
+            PublicFutureTask task = new PublicFutureTask(new NoOpCallable());
+            assertTrue(task.cancel(mayInterruptIfRunning));
+            for (int i = 0; i < 3; i++) {
+                assertFalse(task.runAndReset());
+                assertEquals(0, task.runCount());
+                assertEquals(i + 1, task.runAndResetCount());
+                assertEquals(0, task.setCount());
+                assertEquals(0, task.setExceptionCount());
+            }
+            tryToConfuseDoneTask(task);
+            checkCancelled(task);
+        }
+    }
+
+    /**
+     * setting value causes get to return it
+     */
+    public void testSet() throws Exception {
+        PublicFutureTask task = new PublicFutureTask(new NoOpCallable());
+        task.set(one);
+        for (int i = 0; i < 3; i++) {
+            assertSame(one, task.get());
+            assertSame(one, task.get(LONG_DELAY_MS, MILLISECONDS));
+            assertEquals(1, task.setCount());
+        }
+        tryToConfuseDoneTask(task);
+        checkCompletedNormally(task, one);
+        assertEquals(0, task.runCount());
+    }
+
+    /**
+     * setException causes get to throw ExecutionException
+     */
+    public void testSetException_get() throws Exception {
+        Exception nse = new NoSuchElementException();
+        PublicFutureTask task = new PublicFutureTask(new NoOpCallable());
+        task.setException(nse);
+
+        try {
+            task.get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(nse, success.getCause());
+            checkCompletedAbnormally(task, nse);
+        }
+
+        try {
+            task.get(LONG_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(nse, success.getCause());
+            checkCompletedAbnormally(task, nse);
+        }
+
+        assertEquals(1, task.setExceptionCount());
+        assertEquals(0, task.setCount());
+        tryToConfuseDoneTask(task);
+        checkCompletedAbnormally(task, nse);
+        assertEquals(0, task.runCount());
+    }
+
+    /**
+     * cancel(false) before run succeeds
+     */
+    public void testCancelBeforeRun() {
+        PublicFutureTask task = new PublicFutureTask(new NoOpCallable());
+        assertTrue(task.cancel(false));
+        task.run();
+        assertEquals(0, task.runCount());
+        assertEquals(0, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        assertTrue(task.isCancelled());
+        assertTrue(task.isDone());
+        tryToConfuseDoneTask(task);
+        assertEquals(0, task.runCount());
+        checkCancelled(task);
+    }
+
+    /**
+     * cancel(true) before run succeeds
+     */
+    public void testCancelBeforeRun2() {
+        PublicFutureTask task = new PublicFutureTask(new NoOpCallable());
+        assertTrue(task.cancel(true));
+        task.run();
+        assertEquals(0, task.runCount());
+        assertEquals(0, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        assertTrue(task.isCancelled());
+        assertTrue(task.isDone());
+        tryToConfuseDoneTask(task);
+        assertEquals(0, task.runCount());
+        checkCancelled(task);
+    }
+
+    /**
+     * cancel(false) of a completed task fails
+     */
+    public void testCancelAfterRun() {
+        PublicFutureTask task = new PublicFutureTask(new NoOpCallable());
+        task.run();
+        assertFalse(task.cancel(false));
+        assertEquals(1, task.runCount());
+        assertEquals(1, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        tryToConfuseDoneTask(task);
+        checkCompletedNormally(task, Boolean.TRUE);
+        assertEquals(1, task.runCount());
+    }
+
+    /**
+     * cancel(true) of a completed task fails
+     */
+    public void testCancelAfterRun2() {
+        PublicFutureTask task = new PublicFutureTask(new NoOpCallable());
+        task.run();
+        assertFalse(task.cancel(true));
+        assertEquals(1, task.runCount());
+        assertEquals(1, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        tryToConfuseDoneTask(task);
+        checkCompletedNormally(task, Boolean.TRUE);
+        assertEquals(1, task.runCount());
+    }
+
+    /**
+     * cancel(true) interrupts a running task that subsequently succeeds
+     */
+    public void testCancelInterrupt() {
+        final CountDownLatch pleaseCancel = new CountDownLatch(1);
+        final PublicFutureTask task =
+            new PublicFutureTask(new CheckedRunnable() {
+                public void realRun() {
+                    pleaseCancel.countDown();
+                    try {
+                        delay(LONG_DELAY_MS);
+                        shouldThrow();
+                    } catch (InterruptedException success) {}
+                }});
+
+        Thread t = newStartedThread(task);
+        await(pleaseCancel);
+        assertTrue(task.cancel(true));
+        assertTrue(task.isCancelled());
+        assertTrue(task.isDone());
+        awaitTermination(t);
+        assertEquals(1, task.runCount());
+        assertEquals(1, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        tryToConfuseDoneTask(task);
+        checkCancelled(task);
+    }
+
+    /**
+     * cancel(true) tries to interrupt a running task, but
+     * Thread.interrupt throws (simulating a restrictive security
+     * manager)
+     */
+    public void testCancelInterrupt_ThrowsSecurityException() {
+        final CountDownLatch pleaseCancel = new CountDownLatch(1);
+        final CountDownLatch cancelled = new CountDownLatch(1);
+        final PublicFutureTask task =
+            new PublicFutureTask(new CheckedRunnable() {
+                public void realRun() {
+                    pleaseCancel.countDown();
+                    await(cancelled);
+                    assertFalse(Thread.interrupted());
+                }});
+
+        final Thread t = new Thread(task) {
+            // Simulate a restrictive security manager.
+            @Override public void interrupt() {
+                throw new SecurityException();
+            }};
+        t.setDaemon(true);
+        t.start();
+
+        await(pleaseCancel);
+        try {
+            task.cancel(true);
+            shouldThrow();
+        } catch (SecurityException expected) {}
+
+        // We failed to deliver the interrupt, but the world retains
+        // its sanity, as if we had done task.cancel(false)
+        assertTrue(task.isCancelled());
+        assertTrue(task.isDone());
+        assertEquals(1, task.runCount());
+        assertEquals(1, task.doneCount());
+        assertEquals(0, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        cancelled.countDown();
+        awaitTermination(t);
+        assertEquals(1, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        tryToConfuseDoneTask(task);
+        checkCancelled(task);
+    }
+
+    /**
+     * cancel(true) interrupts a running task that subsequently throws
+     */
+    public void testCancelInterrupt_taskFails() {
+        final CountDownLatch pleaseCancel = new CountDownLatch(1);
+        final PublicFutureTask task =
+            new PublicFutureTask(new Runnable() {
+                public void run() {
+                    pleaseCancel.countDown();
+                    try {
+                        delay(LONG_DELAY_MS);
+                        threadShouldThrow();
+                    } catch (InterruptedException success) {
+                    } catch (Throwable t) { threadUnexpectedException(t); }
+                    throw new RuntimeException();
+                }});
+
+        Thread t = newStartedThread(task);
+        await(pleaseCancel);
+        assertTrue(task.cancel(true));
+        assertTrue(task.isCancelled());
+        awaitTermination(t);
+        assertEquals(1, task.runCount());
+        assertEquals(0, task.setCount());
+        assertEquals(1, task.setExceptionCount());
+        tryToConfuseDoneTask(task);
+        checkCancelled(task);
+    }
+
+    /**
+     * cancel(false) does not interrupt a running task
+     */
+    public void testCancelNoInterrupt() {
+        final CountDownLatch pleaseCancel = new CountDownLatch(1);
+        final CountDownLatch cancelled = new CountDownLatch(1);
+        final PublicFutureTask task =
+            new PublicFutureTask(new CheckedCallable<Boolean>() {
+                public Boolean realCall() {
+                    pleaseCancel.countDown();
+                    await(cancelled);
+                    assertFalse(Thread.interrupted());
+                    return Boolean.TRUE;
+                }});
+
+        Thread t = newStartedThread(task);
+        await(pleaseCancel);
+        assertTrue(task.cancel(false));
+        assertTrue(task.isCancelled());
+        cancelled.countDown();
+        awaitTermination(t);
+        assertEquals(1, task.runCount());
+        assertEquals(1, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        tryToConfuseDoneTask(task);
+        checkCancelled(task);
+    }
+
+    /**
+     * run in one thread causes get in another thread to retrieve value
+     */
+    public void testGetRun() {
+        final CountDownLatch pleaseRun = new CountDownLatch(2);
+
+        final PublicFutureTask task =
+            new PublicFutureTask(new CheckedCallable<Object>() {
+                public Object realCall() {
+                    return two;
+                }});
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                pleaseRun.countDown();
+                assertSame(two, task.get());
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                pleaseRun.countDown();
+                assertSame(two, task.get(2*LONG_DELAY_MS, MILLISECONDS));
+            }});
+
+        await(pleaseRun);
+        checkNotDone(task);
+        assertTrue(t1.isAlive());
+        assertTrue(t2.isAlive());
+        task.run();
+        checkCompletedNormally(task, two);
+        assertEquals(1, task.runCount());
+        assertEquals(1, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        awaitTermination(t1);
+        awaitTermination(t2);
+        tryToConfuseDoneTask(task);
+        checkCompletedNormally(task, two);
+    }
+
+    /**
+     * set in one thread causes get in another thread to retrieve value
+     */
+    public void testGetSet() {
+        final CountDownLatch pleaseSet = new CountDownLatch(2);
+
+        final PublicFutureTask task =
+            new PublicFutureTask(new CheckedCallable<Object>() {
+                public Object realCall() throws InterruptedException {
+                    return two;
+                }});
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                pleaseSet.countDown();
+                assertSame(two, task.get());
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                pleaseSet.countDown();
+                assertSame(two, task.get(2*LONG_DELAY_MS, MILLISECONDS));
+            }});
+
+        await(pleaseSet);
+        checkNotDone(task);
+        assertTrue(t1.isAlive());
+        assertTrue(t2.isAlive());
+        task.set(two);
+        assertEquals(0, task.runCount());
+        assertEquals(1, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        tryToConfuseDoneTask(task);
+        checkCompletedNormally(task, two);
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * Cancelling a task causes timed get in another thread to throw
+     * CancellationException
+     */
+    public void testTimedGet_Cancellation() {
+        testTimedGet_Cancellation(false);
+    }
+    public void testTimedGet_Cancellation_interrupt() {
+        testTimedGet_Cancellation(true);
+    }
+    public void testTimedGet_Cancellation(final boolean mayInterruptIfRunning) {
+        final CountDownLatch pleaseCancel = new CountDownLatch(3);
+        final CountDownLatch cancelled = new CountDownLatch(1);
+        final Callable<Object> callable =
+            new CheckedCallable<Object>() {
+            public Object realCall() throws InterruptedException {
+                pleaseCancel.countDown();
+                if (mayInterruptIfRunning) {
+                    try {
+                        delay(2*LONG_DELAY_MS);
+                    } catch (InterruptedException success) {}
+                } else {
+                    await(cancelled);
+                }
+                return two;
+            }};
+        final PublicFutureTask task = new PublicFutureTask(callable);
+
+        Thread t1 = new ThreadShouldThrow(CancellationException.class) {
+                public void realRun() throws Exception {
+                    pleaseCancel.countDown();
+                    task.get();
+                }};
+        Thread t2 = new ThreadShouldThrow(CancellationException.class) {
+                public void realRun() throws Exception {
+                    pleaseCancel.countDown();
+                    task.get(2*LONG_DELAY_MS, MILLISECONDS);
+                }};
+        t1.start();
+        t2.start();
+        Thread t3 = newStartedThread(task);
+        await(pleaseCancel);
+        checkIsRunning(task);
+        task.cancel(mayInterruptIfRunning);
+        checkCancelled(task);
+        awaitTermination(t1);
+        awaitTermination(t2);
+        cancelled.countDown();
+        awaitTermination(t3);
+        assertEquals(1, task.runCount());
+        assertEquals(1, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        tryToConfuseDoneTask(task);
+        checkCancelled(task);
+    }
+
+    /**
+     * A runtime exception in task causes get to throw ExecutionException
+     */
+    public void testGet_ExecutionException() throws InterruptedException {
+        final ArithmeticException e = new ArithmeticException();
+        final PublicFutureTask task = new PublicFutureTask(new Callable() {
+            public Object call() {
+                throw e;
+            }});
+
+        task.run();
+        assertEquals(1, task.runCount());
+        assertEquals(0, task.setCount());
+        assertEquals(1, task.setExceptionCount());
+        try {
+            task.get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(e, success.getCause());
+            tryToConfuseDoneTask(task);
+            checkCompletedAbnormally(task, success.getCause());
+        }
+    }
+
+    /**
+     * A runtime exception in task causes timed get to throw ExecutionException
+     */
+    public void testTimedGet_ExecutionException2() throws Exception {
+        final ArithmeticException e = new ArithmeticException();
+        final PublicFutureTask task = new PublicFutureTask(new Callable() {
+            public Object call() {
+                throw e;
+            }});
+
+        task.run();
+        try {
+            task.get(LONG_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(e, success.getCause());
+            tryToConfuseDoneTask(task);
+            checkCompletedAbnormally(task, success.getCause());
+        }
+    }
+
+    /**
+     * get is interruptible
+     */
+    public void testGet_interruptible() {
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        final FutureTask task = new FutureTask(new NoOpCallable());
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                Thread.currentThread().interrupt();
+                try {
+                    task.get();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    task.get();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        t.interrupt();
+        awaitTermination(t);
+        checkNotDone(task);
+    }
+
+    /**
+     * timed get is interruptible
+     */
+    public void testTimedGet_interruptible() {
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        final FutureTask task = new FutureTask(new NoOpCallable());
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                Thread.currentThread().interrupt();
+                try {
+                    task.get(2*LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    task.get(2*LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        t.interrupt();
+        awaitTermination(t);
+        checkNotDone(task);
+    }
+
+    /**
+     * A timed out timed get throws TimeoutException
+     */
+    public void testGet_TimeoutException() throws Exception {
+        FutureTask task = new FutureTask(new NoOpCallable());
+        long startTime = System.nanoTime();
+        try {
+            task.get(timeoutMillis(), MILLISECONDS);
+            shouldThrow();
+        } catch (TimeoutException success) {
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        }
+    }
+
+    /**
+     * timed get with null TimeUnit throws NullPointerException
+     */
+    public void testGet_NullTimeUnit() throws Exception {
+        FutureTask task = new FutureTask(new NoOpCallable());
+        long[] timeouts = { Long.MIN_VALUE, 0L, Long.MAX_VALUE };
+
+        for (long timeout : timeouts) {
+            try {
+                task.get(timeout, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+
+        task.run();
+
+        for (long timeout : timeouts) {
+            try {
+                task.get(timeout, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed get with most negative timeout works correctly (i.e. no
+     * underflow bug)
+     */
+    public void testGet_NegativeInfinityTimeout() throws Exception {
+        final ExecutorService pool = Executors.newFixedThreadPool(10);
+        final Runnable nop = new Runnable() { public void run() {}};
+        final FutureTask<Void> task = new FutureTask<>(nop, null);
+        final List<Future<?>> futures = new ArrayList<>();
+        Runnable r = new Runnable() { public void run() {
+            for (long timeout : new long[] { 0L, -1L, Long.MIN_VALUE }) {
+                try {
+                    task.get(timeout, NANOSECONDS);
+                    shouldThrow();
+                } catch (TimeoutException success) {
+                } catch (Throwable fail) {threadUnexpectedException(fail);}}}};
+        for (int i = 0; i < 10; i++)
+            futures.add(pool.submit(r));
+        try {
+            joinPool(pool);
+            for (Future<?> future : futures)
+                checkCompletedNormally(future, null);
+        } finally {
+            task.run();         // last resort to help terminate
+        }
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/JSR166TestCase.java b/jdk/test/java/util/concurrent/tck/JSR166TestCase.java
new file mode 100644
index 00000000000..8063047f8b4
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/JSR166TestCase.java
@@ -0,0 +1,1814 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+/*
+ * @test
+ * @summary JSR-166 tck tests
+ * @build *
+ * @run junit/othervm/timeout=1000 -Djsr166.testImplementationDetails=true JSR166TestCase
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.MINUTES;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadInfo;
+import java.lang.management.ThreadMXBean;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.security.SecurityPermission;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.PropertyPermission;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.Future;
+import java.util.concurrent.RecursiveAction;
+import java.util.concurrent.RecursiveTask;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+
+/**
+ * Base class for JSR166 Junit TCK tests.  Defines some constants,
+ * utility methods and classes, as well as a simple framework for
+ * helping to make sure that assertions failing in generated threads
+ * cause the associated test that generated them to itself fail (which
+ * JUnit does not otherwise arrange).  The rules for creating such
+ * tests are:
+ *
+ * <ol>
+ *
+ * <li>All assertions in code running in generated threads must use
+ * the forms {@link #threadFail}, {@link #threadAssertTrue}, {@link
+ * #threadAssertEquals}, or {@link #threadAssertNull}, (not
+ * {@code fail}, {@code assertTrue}, etc.) It is OK (but not
+ * particularly recommended) for other code to use these forms too.
+ * Only the most typically used JUnit assertion methods are defined
+ * this way, but enough to live with.
+ *
+ * <li>If you override {@link #setUp} or {@link #tearDown}, make sure
+ * to invoke {@code super.setUp} and {@code super.tearDown} within
+ * them. These methods are used to clear and check for thread
+ * assertion failures.
+ *
+ * <li>All delays and timeouts must use one of the constants {@code
+ * SHORT_DELAY_MS}, {@code SMALL_DELAY_MS}, {@code MEDIUM_DELAY_MS},
+ * {@code LONG_DELAY_MS}. The idea here is that a SHORT is always
+ * discriminable from zero time, and always allows enough time for the
+ * small amounts of computation (creating a thread, calling a few
+ * methods, etc) needed to reach a timeout point. Similarly, a SMALL
+ * is always discriminable as larger than SHORT and smaller than
+ * MEDIUM.  And so on. These constants are set to conservative values,
+ * but even so, if there is ever any doubt, they can all be increased
+ * in one spot to rerun tests on slower platforms.
+ *
+ * <li>All threads generated must be joined inside each test case
+ * method (or {@code fail} to do so) before returning from the
+ * method. The {@code joinPool} method can be used to do this when
+ * using Executors.
+ *
+ * </ol>
+ *
+ * <p><b>Other notes</b>
+ * <ul>
+ *
+ * <li>Usually, there is one testcase method per JSR166 method
+ * covering "normal" operation, and then as many exception-testing
+ * methods as there are exceptions the method can throw. Sometimes
+ * there are multiple tests per JSR166 method when the different
+ * "normal" behaviors differ significantly. And sometimes testcases
+ * cover multiple methods when they cannot be tested in isolation.
+ *
+ * <li>The documentation style for testcases is to provide as javadoc
+ * a simple sentence or two describing the property that the testcase
+ * method purports to test. The javadocs do not say anything about how
+ * the property is tested. To find out, read the code.
+ *
+ * <li>These tests are "conformance tests", and do not attempt to
+ * test throughput, latency, scalability or other performance factors
+ * (see the separate "jtreg" tests for a set intended to check these
+ * for the most central aspects of functionality.) So, most tests use
+ * the smallest sensible numbers of threads, collection sizes, etc
+ * needed to check basic conformance.
+ *
+ * <li>The test classes currently do not declare inclusion in
+ * any particular package to simplify things for people integrating
+ * them in TCK test suites.
+ *
+ * <li>As a convenience, the {@code main} of this class (JSR166TestCase)
+ * runs all JSR166 unit tests.
+ *
+ * </ul>
+ */
+public class JSR166TestCase extends TestCase {
+    private static final boolean useSecurityManager =
+        Boolean.getBoolean("jsr166.useSecurityManager");
+
+    protected static final boolean expensiveTests =
+        Boolean.getBoolean("jsr166.expensiveTests");
+
+    /**
+     * If true, also run tests that are not part of the official tck
+     * because they test unspecified implementation details.
+     */
+    protected static final boolean testImplementationDetails =
+        Boolean.getBoolean("jsr166.testImplementationDetails");
+
+    /**
+     * If true, report on stdout all "slow" tests, that is, ones that
+     * take more than profileThreshold milliseconds to execute.
+     */
+    private static final boolean profileTests =
+        Boolean.getBoolean("jsr166.profileTests");
+
+    /**
+     * The number of milliseconds that tests are permitted for
+     * execution without being reported, when profileTests is set.
+     */
+    private static final long profileThreshold =
+        Long.getLong("jsr166.profileThreshold", 100);
+
+    /**
+     * The number of repetitions per test (for tickling rare bugs).
+     */
+    private static final int runsPerTest =
+        Integer.getInteger("jsr166.runsPerTest", 1);
+
+    /**
+     * The number of repetitions of the test suite (for finding leaks?).
+     */
+    private static final int suiteRuns =
+        Integer.getInteger("jsr166.suiteRuns", 1);
+
+    /**
+     * The scaling factor to apply to standard delays used in tests.
+     */
+    private static final int delayFactor =
+        Integer.getInteger("jsr166.delay.factor", 1);
+
+    public JSR166TestCase() { super(); }
+    public JSR166TestCase(String name) { super(name); }
+
+    /**
+     * A filter for tests to run, matching strings of the form
+     * methodName(className), e.g. "testInvokeAll5(ForkJoinPoolTest)"
+     * Usefully combined with jsr166.runsPerTest.
+     */
+    private static final Pattern methodFilter = methodFilter();
+
+    private static Pattern methodFilter() {
+        String regex = System.getProperty("jsr166.methodFilter");
+        return (regex == null) ? null : Pattern.compile(regex);
+    }
+
+    // Instrumentation to debug very rare, but very annoying hung test runs.
+    static volatile TestCase currentTestCase;
+    // static volatile int currentRun = 0;
+    static {
+        Runnable checkForWedgedTest = new Runnable() { public void run() {
+            // Avoid spurious reports with enormous runsPerTest.
+            // A single test case run should never take more than 1 second.
+            // But let's cap it at the high end too ...
+            final int timeoutMinutes =
+                Math.min(15, Math.max(runsPerTest / 60, 1));
+            for (TestCase lastTestCase = currentTestCase;;) {
+                try { MINUTES.sleep(timeoutMinutes); }
+                catch (InterruptedException unexpected) { break; }
+                if (lastTestCase == currentTestCase) {
+                    System.err.printf(
+                        "Looks like we're stuck running test: %s%n",
+                        lastTestCase);
+//                     System.err.printf(
+//                         "Looks like we're stuck running test: %s (%d/%d)%n",
+//                         lastTestCase, currentRun, runsPerTest);
+//                     System.err.println("availableProcessors=" +
+//                         Runtime.getRuntime().availableProcessors());
+//                     System.err.printf("cpu model = %s%n", cpuModel());
+                    dumpTestThreads();
+                    // one stack dump is probably enough; more would be spam
+                    break;
+                }
+                lastTestCase = currentTestCase;
+            }}};
+        Thread thread = new Thread(checkForWedgedTest, "checkForWedgedTest");
+        thread.setDaemon(true);
+        thread.start();
+    }
+
+//     public static String cpuModel() {
+//         try {
+//             Matcher matcher = Pattern.compile("model name\\s*: (.*)")
+//                 .matcher(new String(
+//                      Files.readAllBytes(Paths.get("/proc/cpuinfo")), "UTF-8"));
+//             matcher.find();
+//             return matcher.group(1);
+//         } catch (Exception ex) { return null; }
+//     }
+
+    public void runBare() throws Throwable {
+        currentTestCase = this;
+        if (methodFilter == null
+            || methodFilter.matcher(toString()).find())
+            super.runBare();
+    }
+
+    protected void runTest() throws Throwable {
+        for (int i = 0; i < runsPerTest; i++) {
+            // currentRun = i;
+            if (profileTests)
+                runTestProfiled();
+            else
+                super.runTest();
+        }
+    }
+
+    protected void runTestProfiled() throws Throwable {
+        for (int i = 0; i < 2; i++) {
+            long startTime = System.nanoTime();
+            super.runTest();
+            long elapsedMillis = millisElapsedSince(startTime);
+            if (elapsedMillis < profileThreshold)
+                break;
+            // Never report first run of any test; treat it as a
+            // warmup run, notably to trigger all needed classloading,
+            if (i > 0)
+                System.out.printf("%n%s: %d%n", toString(), elapsedMillis);
+        }
+    }
+
+    /**
+     * Runs all JSR166 unit tests using junit.textui.TestRunner.
+     */
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    static class PithyResultPrinter extends junit.textui.ResultPrinter {
+        PithyResultPrinter(java.io.PrintStream writer) { super(writer); }
+        long runTime;
+        public void startTest(Test test) {}
+        protected void printHeader(long runTime) {
+            this.runTime = runTime; // defer printing for later
+        }
+        protected void printFooter(TestResult result) {
+            if (result.wasSuccessful()) {
+                getWriter().println("OK (" + result.runCount() + " tests)"
+                    + "  Time: " + elapsedTimeAsString(runTime));
+            } else {
+                getWriter().println("Time: " + elapsedTimeAsString(runTime));
+                super.printFooter(result);
+            }
+        }
+    }
+
+    /**
+     * Returns a TestRunner that doesn't bother with unnecessary
+     * fluff, like printing a "." for each test case.
+     */
+    static junit.textui.TestRunner newPithyTestRunner() {
+        junit.textui.TestRunner runner = new junit.textui.TestRunner();
+        runner.setPrinter(new PithyResultPrinter(System.out));
+        return runner;
+    }
+
+    /**
+     * Runs all unit tests in the given test suite.
+     * Actual behavior influenced by jsr166.* system properties.
+     */
+    static void main(Test suite, String[] args) {
+        if (useSecurityManager) {
+            System.err.println("Setting a permissive security manager");
+            Policy.setPolicy(permissivePolicy());
+            System.setSecurityManager(new SecurityManager());
+        }
+        for (int i = 0; i < suiteRuns; i++) {
+            TestResult result = newPithyTestRunner().doRun(suite);
+            if (!result.wasSuccessful())
+                System.exit(1);
+            System.gc();
+            System.runFinalization();
+        }
+    }
+
+    public static TestSuite newTestSuite(Object... suiteOrClasses) {
+        TestSuite suite = new TestSuite();
+        for (Object suiteOrClass : suiteOrClasses) {
+            if (suiteOrClass instanceof TestSuite)
+                suite.addTest((TestSuite) suiteOrClass);
+            else if (suiteOrClass instanceof Class)
+                suite.addTest(new TestSuite((Class<?>) suiteOrClass));
+            else
+                throw new ClassCastException("not a test suite or class");
+        }
+        return suite;
+    }
+
+    public static void addNamedTestClasses(TestSuite suite,
+                                           String... testClassNames) {
+        for (String testClassName : testClassNames) {
+            try {
+                Class<?> testClass = Class.forName(testClassName);
+                Method m = testClass.getDeclaredMethod("suite",
+                                                       new Class<?>[0]);
+                suite.addTest(newTestSuite((Test)m.invoke(null)));
+            } catch (Exception e) {
+                throw new Error("Missing test class", e);
+            }
+        }
+    }
+
+    public static final double JAVA_CLASS_VERSION;
+    public static final String JAVA_SPECIFICATION_VERSION;
+    static {
+        try {
+            JAVA_CLASS_VERSION = java.security.AccessController.doPrivileged(
+                new java.security.PrivilegedAction<Double>() {
+                public Double run() {
+                    return Double.valueOf(System.getProperty("java.class.version"));}});
+            JAVA_SPECIFICATION_VERSION = java.security.AccessController.doPrivileged(
+                new java.security.PrivilegedAction<String>() {
+                public String run() {
+                    return System.getProperty("java.specification.version");}});
+        } catch (Throwable t) {
+            throw new Error(t);
+        }
+    }
+
+    public static boolean atLeastJava6() { return JAVA_CLASS_VERSION >= 50.0; }
+    public static boolean atLeastJava7() { return JAVA_CLASS_VERSION >= 51.0; }
+    public static boolean atLeastJava8() { return JAVA_CLASS_VERSION >= 52.0; }
+    public static boolean atLeastJava9() {
+        return JAVA_CLASS_VERSION >= 53.0
+            // As of 2015-09, java9 still uses 52.0 class file version
+            || JAVA_SPECIFICATION_VERSION.matches("^(1\\.)?(9|[0-9][0-9])$");
+    }
+    public static boolean atLeastJava10() {
+        return JAVA_CLASS_VERSION >= 54.0
+            || JAVA_SPECIFICATION_VERSION.matches("^(1\\.)?[0-9][0-9]$");
+    }
+
+    /**
+     * Collects all JSR166 unit tests as one suite.
+     */
+    public static Test suite() {
+        // Java7+ test classes
+        TestSuite suite = newTestSuite(
+            ForkJoinPoolTest.suite(),
+            ForkJoinTaskTest.suite(),
+            RecursiveActionTest.suite(),
+            RecursiveTaskTest.suite(),
+            LinkedTransferQueueTest.suite(),
+            PhaserTest.suite(),
+            ThreadLocalRandomTest.suite(),
+            AbstractExecutorServiceTest.suite(),
+            AbstractQueueTest.suite(),
+            AbstractQueuedSynchronizerTest.suite(),
+            AbstractQueuedLongSynchronizerTest.suite(),
+            ArrayBlockingQueueTest.suite(),
+            ArrayDequeTest.suite(),
+            AtomicBooleanTest.suite(),
+            AtomicIntegerArrayTest.suite(),
+            AtomicIntegerFieldUpdaterTest.suite(),
+            AtomicIntegerTest.suite(),
+            AtomicLongArrayTest.suite(),
+            AtomicLongFieldUpdaterTest.suite(),
+            AtomicLongTest.suite(),
+            AtomicMarkableReferenceTest.suite(),
+            AtomicReferenceArrayTest.suite(),
+            AtomicReferenceFieldUpdaterTest.suite(),
+            AtomicReferenceTest.suite(),
+            AtomicStampedReferenceTest.suite(),
+            ConcurrentHashMapTest.suite(),
+            ConcurrentLinkedDequeTest.suite(),
+            ConcurrentLinkedQueueTest.suite(),
+            ConcurrentSkipListMapTest.suite(),
+            ConcurrentSkipListSubMapTest.suite(),
+            ConcurrentSkipListSetTest.suite(),
+            ConcurrentSkipListSubSetTest.suite(),
+            CopyOnWriteArrayListTest.suite(),
+            CopyOnWriteArraySetTest.suite(),
+            CountDownLatchTest.suite(),
+            CyclicBarrierTest.suite(),
+            DelayQueueTest.suite(),
+            EntryTest.suite(),
+            ExchangerTest.suite(),
+            ExecutorsTest.suite(),
+            ExecutorCompletionServiceTest.suite(),
+            FutureTaskTest.suite(),
+            LinkedBlockingDequeTest.suite(),
+            LinkedBlockingQueueTest.suite(),
+            LinkedListTest.suite(),
+            LockSupportTest.suite(),
+            PriorityBlockingQueueTest.suite(),
+            PriorityQueueTest.suite(),
+            ReentrantLockTest.suite(),
+            ReentrantReadWriteLockTest.suite(),
+            ScheduledExecutorTest.suite(),
+            ScheduledExecutorSubclassTest.suite(),
+            SemaphoreTest.suite(),
+            SynchronousQueueTest.suite(),
+            SystemTest.suite(),
+            ThreadLocalTest.suite(),
+            ThreadPoolExecutorTest.suite(),
+            ThreadPoolExecutorSubclassTest.suite(),
+            ThreadTest.suite(),
+            TimeUnitTest.suite(),
+            TreeMapTest.suite(),
+            TreeSetTest.suite(),
+            TreeSubMapTest.suite(),
+            TreeSubSetTest.suite());
+
+        // Java8+ test classes
+        if (atLeastJava8()) {
+            String[] java8TestClassNames = {
+                "Atomic8Test",
+                "CompletableFutureTest",
+                "ConcurrentHashMap8Test",
+                "CountedCompleterTest",
+                "DoubleAccumulatorTest",
+                "DoubleAdderTest",
+                "ForkJoinPool8Test",
+                "ForkJoinTask8Test",
+                "LongAccumulatorTest",
+                "LongAdderTest",
+                "SplittableRandomTest",
+                "StampedLockTest",
+                "SubmissionPublisherTest",
+                "ThreadLocalRandom8Test",
+            };
+            addNamedTestClasses(suite, java8TestClassNames);
+        }
+
+        // Java9+ test classes
+        if (atLeastJava9()) {
+            String[] java9TestClassNames = {
+                // Currently empty, but expecting varhandle tests
+            };
+            addNamedTestClasses(suite, java9TestClassNames);
+        }
+
+        return suite;
+    }
+
+    /** Returns list of junit-style test method names in given class. */
+    public static ArrayList<String> testMethodNames(Class<?> testClass) {
+        Method[] methods = testClass.getDeclaredMethods();
+        ArrayList<String> names = new ArrayList<String>(methods.length);
+        for (Method method : methods) {
+            if (method.getName().startsWith("test")
+                && Modifier.isPublic(method.getModifiers())
+                // method.getParameterCount() requires jdk8+
+                && method.getParameterTypes().length == 0) {
+                names.add(method.getName());
+            }
+        }
+        return names;
+    }
+
+    /**
+     * Returns junit-style testSuite for the given test class, but
+     * parameterized by passing extra data to each test.
+     */
+    public static <ExtraData> Test parameterizedTestSuite
+        (Class<? extends JSR166TestCase> testClass,
+         Class<ExtraData> dataClass,
+         ExtraData data) {
+        try {
+            TestSuite suite = new TestSuite();
+            Constructor c =
+                testClass.getDeclaredConstructor(dataClass, String.class);
+            for (String methodName : testMethodNames(testClass))
+                suite.addTest((Test) c.newInstance(data, methodName));
+            return suite;
+        } catch (Exception e) {
+            throw new Error(e);
+        }
+    }
+
+    /**
+     * Returns junit-style testSuite for the jdk8 extension of the
+     * given test class, but parameterized by passing extra data to
+     * each test.  Uses reflection to allow compilation in jdk7.
+     */
+    public static <ExtraData> Test jdk8ParameterizedTestSuite
+        (Class<? extends JSR166TestCase> testClass,
+         Class<ExtraData> dataClass,
+         ExtraData data) {
+        if (atLeastJava8()) {
+            String name = testClass.getName();
+            String name8 = name.replaceAll("Test$", "8Test");
+            if (name.equals(name8)) throw new Error(name);
+            try {
+                return (Test)
+                    Class.forName(name8)
+                    .getMethod("testSuite", new Class[] { dataClass })
+                    .invoke(null, data);
+            } catch (Exception e) {
+                throw new Error(e);
+            }
+        } else {
+            return new TestSuite();
+        }
+    }
+
+    // Delays for timing-dependent tests, in milliseconds.
+
+    public static long SHORT_DELAY_MS;
+    public static long SMALL_DELAY_MS;
+    public static long MEDIUM_DELAY_MS;
+    public static long LONG_DELAY_MS;
+
+    /**
+     * Returns the shortest timed delay. This can be scaled up for
+     * slow machines using the jsr166.delay.factor system property.
+     */
+    protected long getShortDelay() {
+        return 50 * delayFactor;
+    }
+
+    /**
+     * Sets delays as multiples of SHORT_DELAY.
+     */
+    protected void setDelays() {
+        SHORT_DELAY_MS = getShortDelay();
+        SMALL_DELAY_MS  = SHORT_DELAY_MS * 5;
+        MEDIUM_DELAY_MS = SHORT_DELAY_MS * 10;
+        LONG_DELAY_MS   = SHORT_DELAY_MS * 200;
+    }
+
+    /**
+     * Returns a timeout in milliseconds to be used in tests that
+     * verify that operations block or time out.
+     */
+    long timeoutMillis() {
+        return SHORT_DELAY_MS / 4;
+    }
+
+    /**
+     * Returns a new Date instance representing a time at least
+     * delayMillis milliseconds in the future.
+     */
+    Date delayedDate(long delayMillis) {
+        // Add 1 because currentTimeMillis is known to round into the past.
+        return new Date(System.currentTimeMillis() + delayMillis + 1);
+    }
+
+    /**
+     * The first exception encountered if any threadAssertXXX method fails.
+     */
+    private final AtomicReference<Throwable> threadFailure
+        = new AtomicReference<Throwable>(null);
+
+    /**
+     * Records an exception so that it can be rethrown later in the test
+     * harness thread, triggering a test case failure.  Only the first
+     * failure is recorded; subsequent calls to this method from within
+     * the same test have no effect.
+     */
+    public void threadRecordFailure(Throwable t) {
+        System.err.println(t);
+        dumpTestThreads();
+        threadFailure.compareAndSet(null, t);
+    }
+
+    public void setUp() {
+        setDelays();
+    }
+
+    void tearDownFail(String format, Object... args) {
+        String msg = toString() + ": " + String.format(format, args);
+        System.err.println(msg);
+        dumpTestThreads();
+        throw new AssertionFailedError(msg);
+    }
+
+    /**
+     * Extra checks that get done for all test cases.
+     *
+     * Triggers test case failure if any thread assertions have failed,
+     * by rethrowing, in the test harness thread, any exception recorded
+     * earlier by threadRecordFailure.
+     *
+     * Triggers test case failure if interrupt status is set in the main thread.
+     */
+    public void tearDown() throws Exception {
+        Throwable t = threadFailure.getAndSet(null);
+        if (t != null) {
+            if (t instanceof Error)
+                throw (Error) t;
+            else if (t instanceof RuntimeException)
+                throw (RuntimeException) t;
+            else if (t instanceof Exception)
+                throw (Exception) t;
+            else {
+                AssertionFailedError afe =
+                    new AssertionFailedError(t.toString());
+                afe.initCause(t);
+                throw afe;
+            }
+        }
+
+        if (Thread.interrupted())
+            tearDownFail("interrupt status set in main thread");
+
+        checkForkJoinPoolThreadLeaks();
+    }
+
+    /**
+     * Finds missing PoolCleaners
+     */
+    void checkForkJoinPoolThreadLeaks() throws InterruptedException {
+        Thread[] survivors = new Thread[7];
+        int count = Thread.enumerate(survivors);
+        for (int i = 0; i < count; i++) {
+            Thread thread = survivors[i];
+            String name = thread.getName();
+            if (name.startsWith("ForkJoinPool-")) {
+                // give thread some time to terminate
+                thread.join(LONG_DELAY_MS);
+                if (thread.isAlive())
+                    tearDownFail("Found leaked ForkJoinPool thread thread=%s",
+                                 thread);
+            }
+        }
+
+        if (!ForkJoinPool.commonPool()
+            .awaitQuiescence(LONG_DELAY_MS, MILLISECONDS))
+            tearDownFail("ForkJoin common pool thread stuck");
+    }
+
+    /**
+     * Just like fail(reason), but additionally recording (using
+     * threadRecordFailure) any AssertionFailedError thrown, so that
+     * the current testcase will fail.
+     */
+    public void threadFail(String reason) {
+        try {
+            fail(reason);
+        } catch (AssertionFailedError t) {
+            threadRecordFailure(t);
+            throw t;
+        }
+    }
+
+    /**
+     * Just like assertTrue(b), but additionally recording (using
+     * threadRecordFailure) any AssertionFailedError thrown, so that
+     * the current testcase will fail.
+     */
+    public void threadAssertTrue(boolean b) {
+        try {
+            assertTrue(b);
+        } catch (AssertionFailedError t) {
+            threadRecordFailure(t);
+            throw t;
+        }
+    }
+
+    /**
+     * Just like assertFalse(b), but additionally recording (using
+     * threadRecordFailure) any AssertionFailedError thrown, so that
+     * the current testcase will fail.
+     */
+    public void threadAssertFalse(boolean b) {
+        try {
+            assertFalse(b);
+        } catch (AssertionFailedError t) {
+            threadRecordFailure(t);
+            throw t;
+        }
+    }
+
+    /**
+     * Just like assertNull(x), but additionally recording (using
+     * threadRecordFailure) any AssertionFailedError thrown, so that
+     * the current testcase will fail.
+     */
+    public void threadAssertNull(Object x) {
+        try {
+            assertNull(x);
+        } catch (AssertionFailedError t) {
+            threadRecordFailure(t);
+            throw t;
+        }
+    }
+
+    /**
+     * Just like assertEquals(x, y), but additionally recording (using
+     * threadRecordFailure) any AssertionFailedError thrown, so that
+     * the current testcase will fail.
+     */
+    public void threadAssertEquals(long x, long y) {
+        try {
+            assertEquals(x, y);
+        } catch (AssertionFailedError t) {
+            threadRecordFailure(t);
+            throw t;
+        }
+    }
+
+    /**
+     * Just like assertEquals(x, y), but additionally recording (using
+     * threadRecordFailure) any AssertionFailedError thrown, so that
+     * the current testcase will fail.
+     */
+    public void threadAssertEquals(Object x, Object y) {
+        try {
+            assertEquals(x, y);
+        } catch (AssertionFailedError fail) {
+            threadRecordFailure(fail);
+            throw fail;
+        } catch (Throwable fail) {
+            threadUnexpectedException(fail);
+        }
+    }
+
+    /**
+     * Just like assertSame(x, y), but additionally recording (using
+     * threadRecordFailure) any AssertionFailedError thrown, so that
+     * the current testcase will fail.
+     */
+    public void threadAssertSame(Object x, Object y) {
+        try {
+            assertSame(x, y);
+        } catch (AssertionFailedError fail) {
+            threadRecordFailure(fail);
+            throw fail;
+        }
+    }
+
+    /**
+     * Calls threadFail with message "should throw exception".
+     */
+    public void threadShouldThrow() {
+        threadFail("should throw exception");
+    }
+
+    /**
+     * Calls threadFail with message "should throw" + exceptionName.
+     */
+    public void threadShouldThrow(String exceptionName) {
+        threadFail("should throw " + exceptionName);
+    }
+
+    /**
+     * Records the given exception using {@link #threadRecordFailure},
+     * then rethrows the exception, wrapping it in an
+     * AssertionFailedError if necessary.
+     */
+    public void threadUnexpectedException(Throwable t) {
+        threadRecordFailure(t);
+        t.printStackTrace();
+        if (t instanceof RuntimeException)
+            throw (RuntimeException) t;
+        else if (t instanceof Error)
+            throw (Error) t;
+        else {
+            AssertionFailedError afe =
+                new AssertionFailedError("unexpected exception: " + t);
+            afe.initCause(t);
+            throw afe;
+        }
+    }
+
+    /**
+     * Delays, via Thread.sleep, for the given millisecond delay, but
+     * if the sleep is shorter than specified, may re-sleep or yield
+     * until time elapses.  Ensures that the given time, as measured
+     * by System.nanoTime(), has elapsed.
+     */
+    static void delay(long millis) throws InterruptedException {
+        long nanos = millis * (1000 * 1000);
+        final long wakeupTime = System.nanoTime() + nanos;
+        do {
+            if (millis > 0L)
+                Thread.sleep(millis);
+            else // too short to sleep
+                Thread.yield();
+            nanos = wakeupTime - System.nanoTime();
+            millis = nanos / (1000 * 1000);
+        } while (nanos >= 0L);
+    }
+
+    /**
+     * Allows use of try-with-resources with per-test thread pools.
+     */
+    class PoolCleaner implements AutoCloseable {
+        private final ExecutorService pool;
+        public PoolCleaner(ExecutorService pool) { this.pool = pool; }
+        public void close() { joinPool(pool); }
+    }
+
+    /**
+     * An extension of PoolCleaner that has an action to release the pool.
+     */
+    class PoolCleanerWithReleaser extends PoolCleaner {
+        private final Runnable releaser;
+        public PoolCleanerWithReleaser(ExecutorService pool, Runnable releaser) {
+            super(pool);
+            this.releaser = releaser;
+        }
+        public void close() {
+            try {
+                releaser.run();
+            } finally {
+                super.close();
+            }
+        }
+    }
+
+    PoolCleaner cleaner(ExecutorService pool) {
+        return new PoolCleaner(pool);
+    }
+
+    PoolCleaner cleaner(ExecutorService pool, Runnable releaser) {
+        return new PoolCleanerWithReleaser(pool, releaser);
+    }
+
+    PoolCleaner cleaner(ExecutorService pool, CountDownLatch latch) {
+        return new PoolCleanerWithReleaser(pool, releaser(latch));
+    }
+
+    Runnable releaser(final CountDownLatch latch) {
+        return new Runnable() { public void run() {
+            do { latch.countDown(); }
+            while (latch.getCount() > 0);
+        }};
+    }
+
+    /**
+     * Waits out termination of a thread pool or fails doing so.
+     */
+    void joinPool(ExecutorService pool) {
+        try {
+            pool.shutdown();
+            if (!pool.awaitTermination(2 * LONG_DELAY_MS, MILLISECONDS)) {
+                try {
+                    threadFail("ExecutorService " + pool +
+                               " did not terminate in a timely manner");
+                } finally {
+                    // last resort, for the benefit of subsequent tests
+                    pool.shutdownNow();
+                    pool.awaitTermination(MEDIUM_DELAY_MS, MILLISECONDS);
+                }
+            }
+        } catch (SecurityException ok) {
+            // Allowed in case test doesn't have privs
+        } catch (InterruptedException fail) {
+            threadFail("Unexpected InterruptedException");
+        }
+    }
+
+    /** Like Runnable, but with the freedom to throw anything */
+    interface Action { public void run() throws Throwable; }
+
+    /**
+     * Runs all the given actions in parallel, failing if any fail.
+     * Useful for running multiple variants of tests that are
+     * necessarily individually slow because they must block.
+     */
+    void testInParallel(Action ... actions) {
+        ExecutorService pool = Executors.newCachedThreadPool();
+        try (PoolCleaner cleaner = cleaner(pool)) {
+            ArrayList<Future<?>> futures = new ArrayList<>(actions.length);
+            for (final Action action : actions)
+                futures.add(pool.submit(new CheckedRunnable() {
+                    public void realRun() throws Throwable { action.run();}}));
+            for (Future<?> future : futures)
+                try {
+                    assertNull(future.get(LONG_DELAY_MS, MILLISECONDS));
+                } catch (ExecutionException ex) {
+                    threadUnexpectedException(ex.getCause());
+                } catch (Exception ex) {
+                    threadUnexpectedException(ex);
+                }
+        }
+    }
+
+    /**
+     * A debugging tool to print stack traces of most threads, as jstack does.
+     * Uninteresting threads are filtered out.
+     */
+    static void dumpTestThreads() {
+        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
+        System.err.println("------ stacktrace dump start ------");
+        for (ThreadInfo info : threadMXBean.dumpAllThreads(true, true)) {
+            String name = info.getThreadName();
+            if ("Signal Dispatcher".equals(name))
+                continue;
+            if ("Reference Handler".equals(name)
+                && info.getLockName().startsWith("java.lang.ref.Reference$Lock"))
+                continue;
+            if ("Finalizer".equals(name)
+                && info.getLockName().startsWith("java.lang.ref.ReferenceQueue$Lock"))
+                continue;
+            if ("checkForWedgedTest".equals(name))
+                continue;
+            System.err.print(info);
+        }
+        System.err.println("------ stacktrace dump end ------");
+    }
+
+    /**
+     * Checks that thread does not terminate within the default
+     * millisecond delay of {@code timeoutMillis()}.
+     */
+    void assertThreadStaysAlive(Thread thread) {
+        assertThreadStaysAlive(thread, timeoutMillis());
+    }
+
+    /**
+     * Checks that thread does not terminate within the given millisecond delay.
+     */
+    void assertThreadStaysAlive(Thread thread, long millis) {
+        try {
+            // No need to optimize the failing case via Thread.join.
+            delay(millis);
+            assertTrue(thread.isAlive());
+        } catch (InterruptedException fail) {
+            threadFail("Unexpected InterruptedException");
+        }
+    }
+
+    /**
+     * Checks that the threads do not terminate within the default
+     * millisecond delay of {@code timeoutMillis()}.
+     */
+    void assertThreadsStayAlive(Thread... threads) {
+        assertThreadsStayAlive(timeoutMillis(), threads);
+    }
+
+    /**
+     * Checks that the threads do not terminate within the given millisecond delay.
+     */
+    void assertThreadsStayAlive(long millis, Thread... threads) {
+        try {
+            // No need to optimize the failing case via Thread.join.
+            delay(millis);
+            for (Thread thread : threads)
+                assertTrue(thread.isAlive());
+        } catch (InterruptedException fail) {
+            threadFail("Unexpected InterruptedException");
+        }
+    }
+
+    /**
+     * Checks that future.get times out, with the default timeout of
+     * {@code timeoutMillis()}.
+     */
+    void assertFutureTimesOut(Future future) {
+        assertFutureTimesOut(future, timeoutMillis());
+    }
+
+    /**
+     * Checks that future.get times out, with the given millisecond timeout.
+     */
+    void assertFutureTimesOut(Future future, long timeoutMillis) {
+        long startTime = System.nanoTime();
+        try {
+            future.get(timeoutMillis, MILLISECONDS);
+            shouldThrow();
+        } catch (TimeoutException success) {
+        } catch (Exception fail) {
+            threadUnexpectedException(fail);
+        } finally { future.cancel(true); }
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+    }
+
+    /**
+     * Fails with message "should throw exception".
+     */
+    public void shouldThrow() {
+        fail("Should throw exception");
+    }
+
+    /**
+     * Fails with message "should throw " + exceptionName.
+     */
+    public void shouldThrow(String exceptionName) {
+        fail("Should throw " + exceptionName);
+    }
+
+    /**
+     * The number of elements to place in collections, arrays, etc.
+     */
+    public static final int SIZE = 20;
+
+    // Some convenient Integer constants
+
+    public static final Integer zero  = new Integer(0);
+    public static final Integer one   = new Integer(1);
+    public static final Integer two   = new Integer(2);
+    public static final Integer three = new Integer(3);
+    public static final Integer four  = new Integer(4);
+    public static final Integer five  = new Integer(5);
+    public static final Integer six   = new Integer(6);
+    public static final Integer seven = new Integer(7);
+    public static final Integer eight = new Integer(8);
+    public static final Integer nine  = new Integer(9);
+    public static final Integer m1  = new Integer(-1);
+    public static final Integer m2  = new Integer(-2);
+    public static final Integer m3  = new Integer(-3);
+    public static final Integer m4  = new Integer(-4);
+    public static final Integer m5  = new Integer(-5);
+    public static final Integer m6  = new Integer(-6);
+    public static final Integer m10 = new Integer(-10);
+
+    /**
+     * Runs Runnable r with a security policy that permits precisely
+     * the specified permissions.  If there is no current security
+     * manager, the runnable is run twice, both with and without a
+     * security manager.  We require that any security manager permit
+     * getPolicy/setPolicy.
+     */
+    public void runWithPermissions(Runnable r, Permission... permissions) {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm == null) {
+            r.run();
+        }
+        runWithSecurityManagerWithPermissions(r, permissions);
+    }
+
+    /**
+     * Runs Runnable r with a security policy that permits precisely
+     * the specified permissions.  If there is no current security
+     * manager, a temporary one is set for the duration of the
+     * Runnable.  We require that any security manager permit
+     * getPolicy/setPolicy.
+     */
+    public void runWithSecurityManagerWithPermissions(Runnable r,
+                                                      Permission... permissions) {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm == null) {
+            Policy savedPolicy = Policy.getPolicy();
+            try {
+                Policy.setPolicy(permissivePolicy());
+                System.setSecurityManager(new SecurityManager());
+                runWithSecurityManagerWithPermissions(r, permissions);
+            } finally {
+                System.setSecurityManager(null);
+                Policy.setPolicy(savedPolicy);
+            }
+        } else {
+            Policy savedPolicy = Policy.getPolicy();
+            AdjustablePolicy policy = new AdjustablePolicy(permissions);
+            Policy.setPolicy(policy);
+
+            try {
+                r.run();
+            } finally {
+                policy.addPermission(new SecurityPermission("setPolicy"));
+                Policy.setPolicy(savedPolicy);
+            }
+        }
+    }
+
+    /**
+     * Runs a runnable without any permissions.
+     */
+    public void runWithoutPermissions(Runnable r) {
+        runWithPermissions(r);
+    }
+
+    /**
+     * A security policy where new permissions can be dynamically added
+     * or all cleared.
+     */
+    public static class AdjustablePolicy extends java.security.Policy {
+        Permissions perms = new Permissions();
+        AdjustablePolicy(Permission... permissions) {
+            for (Permission permission : permissions)
+                perms.add(permission);
+        }
+        void addPermission(Permission perm) { perms.add(perm); }
+        void clearPermissions() { perms = new Permissions(); }
+        public PermissionCollection getPermissions(CodeSource cs) {
+            return perms;
+        }
+        public PermissionCollection getPermissions(ProtectionDomain pd) {
+            return perms;
+        }
+        public boolean implies(ProtectionDomain pd, Permission p) {
+            return perms.implies(p);
+        }
+        public void refresh() {}
+        public String toString() {
+            List<Permission> ps = new ArrayList<Permission>();
+            for (Enumeration<Permission> e = perms.elements(); e.hasMoreElements();)
+                ps.add(e.nextElement());
+            return "AdjustablePolicy with permissions " + ps;
+        }
+    }
+
+    /**
+     * Returns a policy containing all the permissions we ever need.
+     */
+    public static Policy permissivePolicy() {
+        return new AdjustablePolicy
+            // Permissions j.u.c. needs directly
+            (new RuntimePermission("modifyThread"),
+             new RuntimePermission("getClassLoader"),
+             new RuntimePermission("setContextClassLoader"),
+             // Permissions needed to change permissions!
+             new SecurityPermission("getPolicy"),
+             new SecurityPermission("setPolicy"),
+             new RuntimePermission("setSecurityManager"),
+             // Permissions needed by the junit test harness
+             new RuntimePermission("accessDeclaredMembers"),
+             new PropertyPermission("*", "read"),
+             new java.io.FilePermission("<<ALL FILES>>", "read"));
+    }
+
+    /**
+     * Sleeps until the given time has elapsed.
+     * Throws AssertionFailedError if interrupted.
+     */
+    void sleep(long millis) {
+        try {
+            delay(millis);
+        } catch (InterruptedException fail) {
+            AssertionFailedError afe =
+                new AssertionFailedError("Unexpected InterruptedException");
+            afe.initCause(fail);
+            throw afe;
+        }
+    }
+
+    /**
+     * Spin-waits up to the specified number of milliseconds for the given
+     * thread to enter a wait state: BLOCKED, WAITING, or TIMED_WAITING.
+     */
+    void waitForThreadToEnterWaitState(Thread thread, long timeoutMillis) {
+        long startTime = System.nanoTime();
+        for (;;) {
+            Thread.State s = thread.getState();
+            if (s == Thread.State.BLOCKED ||
+                s == Thread.State.WAITING ||
+                s == Thread.State.TIMED_WAITING)
+                return;
+            else if (s == Thread.State.TERMINATED)
+                fail("Unexpected thread termination");
+            else if (millisElapsedSince(startTime) > timeoutMillis) {
+                threadAssertTrue(thread.isAlive());
+                return;
+            }
+            Thread.yield();
+        }
+    }
+
+    /**
+     * Waits up to LONG_DELAY_MS for the given thread to enter a wait
+     * state: BLOCKED, WAITING, or TIMED_WAITING.
+     */
+    void waitForThreadToEnterWaitState(Thread thread) {
+        waitForThreadToEnterWaitState(thread, LONG_DELAY_MS);
+    }
+
+    /**
+     * Returns the number of milliseconds since time given by
+     * startNanoTime, which must have been previously returned from a
+     * call to {@link System#nanoTime()}.
+     */
+    static long millisElapsedSince(long startNanoTime) {
+        return NANOSECONDS.toMillis(System.nanoTime() - startNanoTime);
+    }
+
+//     void assertTerminatesPromptly(long timeoutMillis, Runnable r) {
+//         long startTime = System.nanoTime();
+//         try {
+//             r.run();
+//         } catch (Throwable fail) { threadUnexpectedException(fail); }
+//         if (millisElapsedSince(startTime) > timeoutMillis/2)
+//             throw new AssertionFailedError("did not return promptly");
+//     }
+
+//     void assertTerminatesPromptly(Runnable r) {
+//         assertTerminatesPromptly(LONG_DELAY_MS/2, r);
+//     }
+
+    /**
+     * Checks that timed f.get() returns the expected value, and does not
+     * wait for the timeout to elapse before returning.
+     */
+    <T> void checkTimedGet(Future<T> f, T expectedValue, long timeoutMillis) {
+        long startTime = System.nanoTime();
+        try {
+            assertEquals(expectedValue, f.get(timeoutMillis, MILLISECONDS));
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        if (millisElapsedSince(startTime) > timeoutMillis/2)
+            throw new AssertionFailedError("timed get did not return promptly");
+    }
+
+    <T> void checkTimedGet(Future<T> f, T expectedValue) {
+        checkTimedGet(f, expectedValue, LONG_DELAY_MS);
+    }
+
+    /**
+     * Returns a new started daemon Thread running the given runnable.
+     */
+    Thread newStartedThread(Runnable runnable) {
+        Thread t = new Thread(runnable);
+        t.setDaemon(true);
+        t.start();
+        return t;
+    }
+
+    /**
+     * Waits for the specified time (in milliseconds) for the thread
+     * to terminate (using {@link Thread#join(long)}), else interrupts
+     * the thread (in the hope that it may terminate later) and fails.
+     */
+    void awaitTermination(Thread t, long timeoutMillis) {
+        try {
+            t.join(timeoutMillis);
+        } catch (InterruptedException fail) {
+            threadUnexpectedException(fail);
+        } finally {
+            if (t.getState() != Thread.State.TERMINATED) {
+                t.interrupt();
+                threadFail("timed out waiting for thread to terminate");
+            }
+        }
+    }
+
+    /**
+     * Waits for LONG_DELAY_MS milliseconds for the thread to
+     * terminate (using {@link Thread#join(long)}), else interrupts
+     * the thread (in the hope that it may terminate later) and fails.
+     */
+    void awaitTermination(Thread t) {
+        awaitTermination(t, LONG_DELAY_MS);
+    }
+
+    // Some convenient Runnable classes
+
+    public abstract class CheckedRunnable implements Runnable {
+        protected abstract void realRun() throws Throwable;
+
+        public final void run() {
+            try {
+                realRun();
+            } catch (Throwable fail) {
+                threadUnexpectedException(fail);
+            }
+        }
+    }
+
+    public abstract class RunnableShouldThrow implements Runnable {
+        protected abstract void realRun() throws Throwable;
+
+        final Class<?> exceptionClass;
+
+        <T extends Throwable> RunnableShouldThrow(Class<T> exceptionClass) {
+            this.exceptionClass = exceptionClass;
+        }
+
+        public final void run() {
+            try {
+                realRun();
+                threadShouldThrow(exceptionClass.getSimpleName());
+            } catch (Throwable t) {
+                if (! exceptionClass.isInstance(t))
+                    threadUnexpectedException(t);
+            }
+        }
+    }
+
+    public abstract class ThreadShouldThrow extends Thread {
+        protected abstract void realRun() throws Throwable;
+
+        final Class<?> exceptionClass;
+
+        <T extends Throwable> ThreadShouldThrow(Class<T> exceptionClass) {
+            this.exceptionClass = exceptionClass;
+        }
+
+        public final void run() {
+            try {
+                realRun();
+                threadShouldThrow(exceptionClass.getSimpleName());
+            } catch (Throwable t) {
+                if (! exceptionClass.isInstance(t))
+                    threadUnexpectedException(t);
+            }
+        }
+    }
+
+    public abstract class CheckedInterruptedRunnable implements Runnable {
+        protected abstract void realRun() throws Throwable;
+
+        public final void run() {
+            try {
+                realRun();
+                threadShouldThrow("InterruptedException");
+            } catch (InterruptedException success) {
+                threadAssertFalse(Thread.interrupted());
+            } catch (Throwable fail) {
+                threadUnexpectedException(fail);
+            }
+        }
+    }
+
+    public abstract class CheckedCallable<T> implements Callable<T> {
+        protected abstract T realCall() throws Throwable;
+
+        public final T call() {
+            try {
+                return realCall();
+            } catch (Throwable fail) {
+                threadUnexpectedException(fail);
+                return null;
+            }
+        }
+    }
+
+    public abstract class CheckedInterruptedCallable<T>
+        implements Callable<T> {
+        protected abstract T realCall() throws Throwable;
+
+        public final T call() {
+            try {
+                T result = realCall();
+                threadShouldThrow("InterruptedException");
+                return result;
+            } catch (InterruptedException success) {
+                threadAssertFalse(Thread.interrupted());
+            } catch (Throwable fail) {
+                threadUnexpectedException(fail);
+            }
+            return null;
+        }
+    }
+
+    public static class NoOpRunnable implements Runnable {
+        public void run() {}
+    }
+
+    public static class NoOpCallable implements Callable {
+        public Object call() { return Boolean.TRUE; }
+    }
+
+    public static final String TEST_STRING = "a test string";
+
+    public static class StringTask implements Callable<String> {
+        final String value;
+        public StringTask() { this(TEST_STRING); }
+        public StringTask(String value) { this.value = value; }
+        public String call() { return value; }
+    }
+
+    public Callable<String> latchAwaitingStringTask(final CountDownLatch latch) {
+        return new CheckedCallable<String>() {
+            protected String realCall() {
+                try {
+                    latch.await();
+                } catch (InterruptedException quittingTime) {}
+                return TEST_STRING;
+            }};
+    }
+
+    public Runnable countDowner(final CountDownLatch latch) {
+        return new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                latch.countDown();
+            }};
+    }
+
+    class LatchAwaiter extends CheckedRunnable {
+        static final int NEW = 0;
+        static final int RUNNING = 1;
+        static final int DONE = 2;
+        final CountDownLatch latch;
+        int state = NEW;
+        LatchAwaiter(CountDownLatch latch) { this.latch = latch; }
+        public void realRun() throws InterruptedException {
+            state = 1;
+            await(latch);
+            state = 2;
+        }
+    }
+
+    public LatchAwaiter awaiter(CountDownLatch latch) {
+        return new LatchAwaiter(latch);
+    }
+
+    public void await(CountDownLatch latch) {
+        try {
+            if (!latch.await(LONG_DELAY_MS, MILLISECONDS))
+                fail("timed out waiting for CountDownLatch for "
+                     + (LONG_DELAY_MS/1000) + " sec");
+        } catch (Throwable fail) {
+            threadUnexpectedException(fail);
+        }
+    }
+
+    public void await(Semaphore semaphore) {
+        try {
+            if (!semaphore.tryAcquire(LONG_DELAY_MS, MILLISECONDS))
+                fail("timed out waiting for Semaphore for "
+                     + (LONG_DELAY_MS/1000) + " sec");
+        } catch (Throwable fail) {
+            threadUnexpectedException(fail);
+        }
+    }
+
+//     /**
+//      * Spin-waits up to LONG_DELAY_MS until flag becomes true.
+//      */
+//     public void await(AtomicBoolean flag) {
+//         await(flag, LONG_DELAY_MS);
+//     }
+
+//     /**
+//      * Spin-waits up to the specified timeout until flag becomes true.
+//      */
+//     public void await(AtomicBoolean flag, long timeoutMillis) {
+//         long startTime = System.nanoTime();
+//         while (!flag.get()) {
+//             if (millisElapsedSince(startTime) > timeoutMillis)
+//                 throw new AssertionFailedError("timed out");
+//             Thread.yield();
+//         }
+//     }
+
+    public static class NPETask implements Callable<String> {
+        public String call() { throw new NullPointerException(); }
+    }
+
+    public static class CallableOne implements Callable<Integer> {
+        public Integer call() { return one; }
+    }
+
+    public class ShortRunnable extends CheckedRunnable {
+        protected void realRun() throws Throwable {
+            delay(SHORT_DELAY_MS);
+        }
+    }
+
+    public class ShortInterruptedRunnable extends CheckedInterruptedRunnable {
+        protected void realRun() throws InterruptedException {
+            delay(SHORT_DELAY_MS);
+        }
+    }
+
+    public class SmallRunnable extends CheckedRunnable {
+        protected void realRun() throws Throwable {
+            delay(SMALL_DELAY_MS);
+        }
+    }
+
+    public class SmallPossiblyInterruptedRunnable extends CheckedRunnable {
+        protected void realRun() {
+            try {
+                delay(SMALL_DELAY_MS);
+            } catch (InterruptedException ok) {}
+        }
+    }
+
+    public class SmallCallable extends CheckedCallable {
+        protected Object realCall() throws InterruptedException {
+            delay(SMALL_DELAY_MS);
+            return Boolean.TRUE;
+        }
+    }
+
+    public class MediumRunnable extends CheckedRunnable {
+        protected void realRun() throws Throwable {
+            delay(MEDIUM_DELAY_MS);
+        }
+    }
+
+    public class MediumInterruptedRunnable extends CheckedInterruptedRunnable {
+        protected void realRun() throws InterruptedException {
+            delay(MEDIUM_DELAY_MS);
+        }
+    }
+
+    public Runnable possiblyInterruptedRunnable(final long timeoutMillis) {
+        return new CheckedRunnable() {
+            protected void realRun() {
+                try {
+                    delay(timeoutMillis);
+                } catch (InterruptedException ok) {}
+            }};
+    }
+
+    public class MediumPossiblyInterruptedRunnable extends CheckedRunnable {
+        protected void realRun() {
+            try {
+                delay(MEDIUM_DELAY_MS);
+            } catch (InterruptedException ok) {}
+        }
+    }
+
+    public class LongPossiblyInterruptedRunnable extends CheckedRunnable {
+        protected void realRun() {
+            try {
+                delay(LONG_DELAY_MS);
+            } catch (InterruptedException ok) {}
+        }
+    }
+
+    /**
+     * For use as ThreadFactory in constructors
+     */
+    public static class SimpleThreadFactory implements ThreadFactory {
+        public Thread newThread(Runnable r) {
+            return new Thread(r);
+        }
+    }
+
+    public interface TrackedRunnable extends Runnable {
+        boolean isDone();
+    }
+
+    public static TrackedRunnable trackedRunnable(final long timeoutMillis) {
+        return new TrackedRunnable() {
+                private volatile boolean done = false;
+                public boolean isDone() { return done; }
+                public void run() {
+                    try {
+                        delay(timeoutMillis);
+                        done = true;
+                    } catch (InterruptedException ok) {}
+                }
+            };
+    }
+
+    public static class TrackedShortRunnable implements Runnable {
+        public volatile boolean done = false;
+        public void run() {
+            try {
+                delay(SHORT_DELAY_MS);
+                done = true;
+            } catch (InterruptedException ok) {}
+        }
+    }
+
+    public static class TrackedSmallRunnable implements Runnable {
+        public volatile boolean done = false;
+        public void run() {
+            try {
+                delay(SMALL_DELAY_MS);
+                done = true;
+            } catch (InterruptedException ok) {}
+        }
+    }
+
+    public static class TrackedMediumRunnable implements Runnable {
+        public volatile boolean done = false;
+        public void run() {
+            try {
+                delay(MEDIUM_DELAY_MS);
+                done = true;
+            } catch (InterruptedException ok) {}
+        }
+    }
+
+    public static class TrackedLongRunnable implements Runnable {
+        public volatile boolean done = false;
+        public void run() {
+            try {
+                delay(LONG_DELAY_MS);
+                done = true;
+            } catch (InterruptedException ok) {}
+        }
+    }
+
+    public static class TrackedNoOpRunnable implements Runnable {
+        public volatile boolean done = false;
+        public void run() {
+            done = true;
+        }
+    }
+
+    public static class TrackedCallable implements Callable {
+        public volatile boolean done = false;
+        public Object call() {
+            try {
+                delay(SMALL_DELAY_MS);
+                done = true;
+            } catch (InterruptedException ok) {}
+            return Boolean.TRUE;
+        }
+    }
+
+    /**
+     * Analog of CheckedRunnable for RecursiveAction
+     */
+    public abstract class CheckedRecursiveAction extends RecursiveAction {
+        protected abstract void realCompute() throws Throwable;
+
+        @Override protected final void compute() {
+            try {
+                realCompute();
+            } catch (Throwable fail) {
+                threadUnexpectedException(fail);
+            }
+        }
+    }
+
+    /**
+     * Analog of CheckedCallable for RecursiveTask
+     */
+    public abstract class CheckedRecursiveTask<T> extends RecursiveTask<T> {
+        protected abstract T realCompute() throws Throwable;
+
+        @Override protected final T compute() {
+            try {
+                return realCompute();
+            } catch (Throwable fail) {
+                threadUnexpectedException(fail);
+                return null;
+            }
+        }
+    }
+
+    /**
+     * For use as RejectedExecutionHandler in constructors
+     */
+    public static class NoOpREHandler implements RejectedExecutionHandler {
+        public void rejectedExecution(Runnable r,
+                                      ThreadPoolExecutor executor) {}
+    }
+
+    /**
+     * A CyclicBarrier that uses timed await and fails with
+     * AssertionFailedErrors instead of throwing checked exceptions.
+     */
+    public class CheckedBarrier extends CyclicBarrier {
+        public CheckedBarrier(int parties) { super(parties); }
+
+        public int await() {
+            try {
+                return super.await(2 * LONG_DELAY_MS, MILLISECONDS);
+            } catch (TimeoutException timedOut) {
+                throw new AssertionFailedError("timed out");
+            } catch (Exception fail) {
+                AssertionFailedError afe =
+                    new AssertionFailedError("Unexpected exception: " + fail);
+                afe.initCause(fail);
+                throw afe;
+            }
+        }
+    }
+
+    void checkEmpty(BlockingQueue q) {
+        try {
+            assertTrue(q.isEmpty());
+            assertEquals(0, q.size());
+            assertNull(q.peek());
+            assertNull(q.poll());
+            assertNull(q.poll(0, MILLISECONDS));
+            assertEquals(q.toString(), "[]");
+            assertTrue(Arrays.equals(q.toArray(), new Object[0]));
+            assertFalse(q.iterator().hasNext());
+            try {
+                q.element();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+            try {
+                q.iterator().next();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+            try {
+                q.remove();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+        } catch (InterruptedException fail) { threadUnexpectedException(fail); }
+    }
+
+    void assertSerialEquals(Object x, Object y) {
+        assertTrue(Arrays.equals(serialBytes(x), serialBytes(y)));
+    }
+
+    void assertNotSerialEquals(Object x, Object y) {
+        assertFalse(Arrays.equals(serialBytes(x), serialBytes(y)));
+    }
+
+    byte[] serialBytes(Object o) {
+        try {
+            ByteArrayOutputStream bos = new ByteArrayOutputStream();
+            ObjectOutputStream oos = new ObjectOutputStream(bos);
+            oos.writeObject(o);
+            oos.flush();
+            oos.close();
+            return bos.toByteArray();
+        } catch (Throwable fail) {
+            threadUnexpectedException(fail);
+            return new byte[0];
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    <T> T serialClone(T o) {
+        try {
+            ObjectInputStream ois = new ObjectInputStream
+                (new ByteArrayInputStream(serialBytes(o)));
+            T clone = (T) ois.readObject();
+            assertSame(o.getClass(), clone.getClass());
+            return clone;
+        } catch (Throwable fail) {
+            threadUnexpectedException(fail);
+            return null;
+        }
+    }
+
+    public void assertThrows(Class<? extends Throwable> expectedExceptionClass,
+                             Runnable... throwingActions) {
+        for (Runnable throwingAction : throwingActions) {
+            boolean threw = false;
+            try { throwingAction.run(); }
+            catch (Throwable t) {
+                threw = true;
+                if (!expectedExceptionClass.isInstance(t)) {
+                    AssertionFailedError afe =
+                        new AssertionFailedError
+                        ("Expected " + expectedExceptionClass.getName() +
+                         ", got " + t.getClass().getName());
+                    afe.initCause(t);
+                    threadUnexpectedException(afe);
+                }
+            }
+            if (!threw)
+                shouldThrow(expectedExceptionClass.getName());
+        }
+    }
+
+    public void assertIteratorExhausted(Iterator<?> it) {
+        try {
+            it.next();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertFalse(it.hasNext());
+    }
+}
diff --git a/jdk/test/java/util/concurrent/tck/LinkedBlockingDequeTest.java b/jdk/test/java/util/concurrent/tck/LinkedBlockingDequeTest.java
new file mode 100644
index 00000000000..138799035cb
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/LinkedBlockingDequeTest.java
@@ -0,0 +1,1848 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.concurrent.BlockingDeque;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingDeque;
+
+import junit.framework.Test;
+
+public class LinkedBlockingDequeTest extends JSR166TestCase {
+
+    public static class Unbounded extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new LinkedBlockingDeque();
+        }
+    }
+
+    public static class Bounded extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new LinkedBlockingDeque(SIZE);
+        }
+    }
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return newTestSuite(LinkedBlockingDequeTest.class,
+                            new Unbounded().testSuite(),
+                            new Bounded().testSuite());
+    }
+
+    /**
+     * Returns a new deque of given size containing consecutive
+     * Integers 0 ... n.
+     */
+    private LinkedBlockingDeque<Integer> populatedDeque(int n) {
+        LinkedBlockingDeque<Integer> q =
+            new LinkedBlockingDeque<Integer>(n);
+        assertTrue(q.isEmpty());
+        for (int i = 0; i < n; i++)
+            assertTrue(q.offer(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(0, q.remainingCapacity());
+        assertEquals(n, q.size());
+        return q;
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque();
+        assertTrue(q.isEmpty());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.add(new Integer(2));
+        q.removeFirst();
+        q.removeFirst();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testSize() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i, q.size());
+            q.removeFirst();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * offerFirst(null) throws NullPointerException
+     */
+    public void testOfferFirstNull() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque();
+        try {
+            q.offerFirst(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * offerLast(null) throws NullPointerException
+     */
+    public void testOfferLastNull() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque();
+        try {
+            q.offerLast(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * OfferFirst succeeds
+     */
+    public void testOfferFirst() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque();
+        assertTrue(q.offerFirst(new Integer(0)));
+        assertTrue(q.offerFirst(new Integer(1)));
+    }
+
+    /**
+     * OfferLast succeeds
+     */
+    public void testOfferLast() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque();
+        assertTrue(q.offerLast(new Integer(0)));
+        assertTrue(q.offerLast(new Integer(1)));
+    }
+
+    /**
+     * pollFirst succeeds unless empty
+     */
+    public void testPollFirst() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * pollLast succeeds unless empty
+     */
+    public void testPollLast() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.pollLast());
+        }
+        assertNull(q.pollLast());
+    }
+
+    /**
+     * peekFirst returns next element, or null if empty
+     */
+    public void testPeekFirst() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peekFirst());
+            assertEquals(i, q.pollFirst());
+            assertTrue(q.peekFirst() == null ||
+                       !q.peekFirst().equals(i));
+        }
+        assertNull(q.peekFirst());
+    }
+
+    /**
+     * peek returns next element, or null if empty
+     */
+    public void testPeek() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peek());
+            assertEquals(i, q.pollFirst());
+            assertTrue(q.peek() == null ||
+                       !q.peek().equals(i));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * peekLast returns next element, or null if empty
+     */
+    public void testPeekLast() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.peekLast());
+            assertEquals(i, q.pollLast());
+            assertTrue(q.peekLast() == null ||
+                       !q.peekLast().equals(i));
+        }
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * getFirst() returns first element, or throws NSEE if empty
+     */
+    public void testFirstElement() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.getFirst());
+            assertEquals(i, q.pollFirst());
+        }
+        try {
+            q.getFirst();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekFirst());
+    }
+
+    /**
+     * getLast() returns last element, or throws NSEE if empty
+     */
+    public void testLastElement() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.getLast());
+            assertEquals(i, q.pollLast());
+        }
+        try {
+            q.getLast();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * removeFirst() removes first element, or throws NSEE if empty
+     */
+    public void testRemoveFirst() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.removeFirst());
+        }
+        try {
+            q.removeFirst();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekFirst());
+    }
+
+    /**
+     * removeLast() removes last element, or throws NSEE if empty
+     */
+    public void testRemoveLast() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.removeLast());
+        }
+        try {
+            q.removeLast();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * remove removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * removeFirstOccurrence(x) removes x and returns true if present
+     */
+    public void testRemoveFirstOccurrence() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.removeFirstOccurrence(new Integer(i)));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.removeFirstOccurrence(new Integer(i)));
+            assertFalse(q.removeFirstOccurrence(new Integer(i + 1)));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * removeLastOccurrence(x) removes x and returns true if present
+     */
+    public void testRemoveLastOccurrence() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.removeLastOccurrence(new Integer(i)));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.removeLastOccurrence(new Integer(i)));
+            assertFalse(q.removeLastOccurrence(new Integer(i + 1)));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * peekFirst returns element inserted with addFirst
+     */
+    public void testAddFirst() {
+        LinkedBlockingDeque q = populatedDeque(3);
+        q.pollLast();
+        q.addFirst(four);
+        assertSame(four, q.peekFirst());
+    }
+
+    /**
+     * peekLast returns element inserted with addLast
+     */
+    public void testAddLast() {
+        LinkedBlockingDeque q = populatedDeque(3);
+        q.pollLast();
+        q.addLast(four);
+        assertSame(four, q.peekLast());
+    }
+
+    /**
+     * A new deque has the indicated capacity, or Integer.MAX_VALUE if
+     * none given
+     */
+    public void testConstructor1() {
+        assertEquals(SIZE, new LinkedBlockingDeque(SIZE).remainingCapacity());
+        assertEquals(Integer.MAX_VALUE, new LinkedBlockingDeque().remainingCapacity());
+    }
+
+    /**
+     * Constructor throws IllegalArgumentException if capacity argument nonpositive
+     */
+    public void testConstructor2() {
+        try {
+            new LinkedBlockingDeque(0);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Initializing from null Collection throws NullPointerException
+     */
+    public void testConstructor3() {
+        try {
+            new LinkedBlockingDeque(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NullPointerException
+     */
+    public void testConstructor4() {
+        Collection<Integer> elements = Arrays.asList(new Integer[SIZE]);
+        try {
+            new LinkedBlockingDeque(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws
+     * NullPointerException
+     */
+    public void testConstructor5() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = i;
+        Collection<Integer> elements = Arrays.asList(ints);
+        try {
+            new LinkedBlockingDeque(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Deque contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = i;
+        LinkedBlockingDeque q = new LinkedBlockingDeque(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * Deque transitions from empty to full when elements added
+     */
+    public void testEmptyFull() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(2);
+        assertTrue(q.isEmpty());
+        assertEquals("should have room for 2", 2, q.remainingCapacity());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        q.add(two);
+        assertFalse(q.isEmpty());
+        assertEquals(0, q.remainingCapacity());
+        assertFalse(q.offer(three));
+    }
+
+    /**
+     * remainingCapacity decreases on add, increases on remove
+     */
+    public void testRemainingCapacity() {
+        BlockingQueue q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remainingCapacity());
+            assertEquals(SIZE, q.size() + q.remainingCapacity());
+            assertEquals(i, q.remove());
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i, q.remainingCapacity());
+            assertEquals(SIZE, q.size() + q.remainingCapacity());
+            assertTrue(q.add(i));
+        }
+    }
+
+    /**
+     * push(null) throws NPE
+     */
+    public void testPushNull() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(1);
+        try {
+            q.push(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * push succeeds if not full; throws ISE if full
+     */
+    public void testPush() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            Integer x = new Integer(i);
+            q.push(x);
+            assertEquals(x, q.peek());
+        }
+        assertEquals(0, q.remainingCapacity());
+        try {
+            q.push(new Integer(SIZE));
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * peekFirst returns element inserted with push
+     */
+    public void testPushWithPeek() {
+        LinkedBlockingDeque q = populatedDeque(3);
+        q.pollLast();
+        q.push(four);
+        assertSame(four, q.peekFirst());
+    }
+
+    /**
+     * pop removes next element, or throws NSEE if empty
+     */
+    public void testPop() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pop());
+        }
+        try {
+            q.pop();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * Offer succeeds if not full; fails if full
+     */
+    public void testOffer() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(1);
+        assertTrue(q.offer(zero));
+        assertFalse(q.offer(one));
+    }
+
+    /**
+     * add succeeds if not full; throws ISE if full
+     */
+    public void testAdd() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i)
+            assertTrue(q.add(new Integer(i)));
+        assertEquals(0, q.remainingCapacity());
+        try {
+            q.add(new Integer(SIZE));
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * addAll(this) throws IAE
+     */
+    public void testAddAllSelf() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        try {
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        Collection<Integer> elements = Arrays.asList(ints);
+        try {
+            q.addAll(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll throws IllegalStateException if not enough room
+     */
+    public void testAddAll4() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE - 1);
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        Collection<Integer> elements = Arrays.asList(ints);
+        try {
+            q.addAll(elements);
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * Deque contains all elements, in traversal order, of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * all elements successfully put are contained
+     */
+    public void testPut() throws InterruptedException {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            Integer x = new Integer(i);
+            q.put(x);
+            assertTrue(q.contains(x));
+        }
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * put blocks interruptibly if full
+     */
+    public void testBlockingPut() throws InterruptedException {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i)
+                    q.put(i);
+                assertEquals(SIZE, q.size());
+                assertEquals(0, q.remainingCapacity());
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(SIZE, q.size());
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * put blocks interruptibly waiting for take when full
+     */
+    public void testPutWithTake() throws InterruptedException {
+        final int capacity = 2;
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(capacity);
+        final CountDownLatch pleaseTake = new CountDownLatch(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < capacity; i++)
+                    q.put(i);
+                pleaseTake.countDown();
+                q.put(86);
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseTake);
+        assertEquals(0, q.remainingCapacity());
+        assertEquals(0, q.take());
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * timed offer times out if full and elements not taken
+     */
+    public void testTimedOffer() throws InterruptedException {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(2);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(new Object());
+                q.put(new Object());
+                long startTime = System.nanoTime();
+                assertFalse(q.offer(new Object(), timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                pleaseInterrupt.countDown();
+                try {
+                    q.offer(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * take retrieves elements in FIFO order
+     */
+    public void testTake() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.take());
+        }
+    }
+
+    /**
+     * take removes existing elements until empty, then blocks interruptibly
+     */
+    public void testBlockingTake() throws InterruptedException {
+        final LinkedBlockingDeque q = populatedDeque(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(i, q.take());
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll());
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * timed poll with zero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll0() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll(0, MILLISECONDS));
+        }
+        assertNull(q.poll(0, MILLISECONDS));
+    }
+
+    /**
+     * timed poll with nonzero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            long startTime = System.nanoTime();
+            assertEquals(i, q.poll(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+        long startTime = System.nanoTime();
+        assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        checkEmpty(q);
+    }
+
+    /**
+     * Interrupted timed poll throws InterruptedException instead of
+     * returning timeout status
+     */
+    public void testInterruptedTimedPoll() throws InterruptedException {
+        final BlockingQueue<Integer> q = populatedDeque(SIZE);
+        final CountDownLatch aboutToWait = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
+                }
+                aboutToWait.countDown();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {
+                    assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+                }
+            }});
+
+        aboutToWait.await();
+        waitForThreadToEnterWaitState(t, LONG_DELAY_MS);
+        t.interrupt();
+        awaitTermination(t);
+        checkEmpty(q);
+    }
+
+    /**
+     * putFirst(null) throws NPE
+     */
+    public void testPutFirstNull() throws InterruptedException {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        try {
+            q.putFirst(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * all elements successfully putFirst are contained
+     */
+    public void testPutFirst() throws InterruptedException {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            Integer x = new Integer(i);
+            q.putFirst(x);
+            assertTrue(q.contains(x));
+        }
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * putFirst blocks interruptibly if full
+     */
+    public void testBlockingPutFirst() throws InterruptedException {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i)
+                    q.putFirst(i);
+                assertEquals(SIZE, q.size());
+                assertEquals(0, q.remainingCapacity());
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.putFirst(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.putFirst(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(SIZE, q.size());
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * putFirst blocks interruptibly waiting for take when full
+     */
+    public void testPutFirstWithTake() throws InterruptedException {
+        final int capacity = 2;
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(capacity);
+        final CountDownLatch pleaseTake = new CountDownLatch(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < capacity; i++)
+                    q.putFirst(i);
+                pleaseTake.countDown();
+                q.putFirst(86);
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.putFirst(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseTake);
+        assertEquals(0, q.remainingCapacity());
+        assertEquals(capacity - 1, q.take());
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * timed offerFirst times out if full and elements not taken
+     */
+    public void testTimedOfferFirst() throws InterruptedException {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(2);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.putFirst(new Object());
+                q.putFirst(new Object());
+                long startTime = System.nanoTime();
+                assertFalse(q.offerFirst(new Object(), timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                pleaseInterrupt.countDown();
+                try {
+                    q.offerFirst(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * take retrieves elements in FIFO order
+     */
+    public void testTakeFirst() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.takeFirst());
+        }
+    }
+
+    /**
+     * takeFirst() blocks interruptibly when empty
+     */
+    public void testTakeFirstFromEmptyBlocksInterruptibly() {
+        final BlockingDeque q = new LinkedBlockingDeque();
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                threadStarted.countDown();
+                try {
+                    q.takeFirst();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(threadStarted);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * takeFirst() throws InterruptedException immediately if interrupted
+     * before waiting
+     */
+    public void testTakeFirstFromEmptyAfterInterrupt() {
+        final BlockingDeque q = new LinkedBlockingDeque();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                Thread.currentThread().interrupt();
+                try {
+                    q.takeFirst();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * takeLast() blocks interruptibly when empty
+     */
+    public void testTakeLastFromEmptyBlocksInterruptibly() {
+        final BlockingDeque q = new LinkedBlockingDeque();
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                threadStarted.countDown();
+                try {
+                    q.takeLast();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(threadStarted);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * takeLast() throws InterruptedException immediately if interrupted
+     * before waiting
+     */
+    public void testTakeLastFromEmptyAfterInterrupt() {
+        final BlockingDeque q = new LinkedBlockingDeque();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                Thread.currentThread().interrupt();
+                try {
+                    q.takeLast();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * takeFirst removes existing elements until empty, then blocks interruptibly
+     */
+    public void testBlockingTakeFirst() throws InterruptedException {
+        final LinkedBlockingDeque q = populatedDeque(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(i, q.takeFirst());
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.takeFirst();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.takeFirst();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * timed pollFirst with zero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPollFirst0() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst(0, MILLISECONDS));
+        }
+        assertNull(q.pollFirst(0, MILLISECONDS));
+    }
+
+    /**
+     * timed pollFirst with nonzero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPollFirst() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            long startTime = System.nanoTime();
+            assertEquals(i, q.pollFirst(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+        long startTime = System.nanoTime();
+        assertNull(q.pollFirst(timeoutMillis(), MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        checkEmpty(q);
+    }
+
+    /**
+     * Interrupted timed pollFirst throws InterruptedException instead of
+     * returning timeout status
+     */
+    public void testInterruptedTimedPollFirst() throws InterruptedException {
+        final LinkedBlockingDeque q = populatedDeque(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(i, q.pollFirst(LONG_DELAY_MS, MILLISECONDS));
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.pollFirst(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.pollFirst(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+                assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * timed pollFirst before a delayed offerFirst fails; after offerFirst succeeds;
+     * on interruption throws
+     */
+    public void testTimedPollFirstWithOfferFirst() throws InterruptedException {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(2);
+        final CheckedBarrier barrier = new CheckedBarrier(2);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                assertNull(q.pollFirst(timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+
+                barrier.await();
+
+                assertSame(zero, q.pollFirst(LONG_DELAY_MS, MILLISECONDS));
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.pollFirst(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+
+                barrier.await();
+                try {
+                    q.pollFirst(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+            }});
+
+        barrier.await();
+        long startTime = System.nanoTime();
+        assertTrue(q.offerFirst(zero, LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        barrier.await();
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * putLast(null) throws NPE
+     */
+    public void testPutLastNull() throws InterruptedException {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        try {
+            q.putLast(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * all elements successfully putLast are contained
+     */
+    public void testPutLast() throws InterruptedException {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            Integer x = new Integer(i);
+            q.putLast(x);
+            assertTrue(q.contains(x));
+        }
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * putLast blocks interruptibly if full
+     */
+    public void testBlockingPutLast() throws InterruptedException {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i)
+                    q.putLast(i);
+                assertEquals(SIZE, q.size());
+                assertEquals(0, q.remainingCapacity());
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.putLast(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.putLast(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(SIZE, q.size());
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * putLast blocks interruptibly waiting for take when full
+     */
+    public void testPutLastWithTake() throws InterruptedException {
+        final int capacity = 2;
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(capacity);
+        final CountDownLatch pleaseTake = new CountDownLatch(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < capacity; i++)
+                    q.putLast(i);
+                pleaseTake.countDown();
+                q.putLast(86);
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.putLast(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseTake);
+        assertEquals(0, q.remainingCapacity());
+        assertEquals(0, q.take());
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * timed offerLast times out if full and elements not taken
+     */
+    public void testTimedOfferLast() throws InterruptedException {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(2);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.putLast(new Object());
+                q.putLast(new Object());
+                long startTime = System.nanoTime();
+                assertFalse(q.offerLast(new Object(), timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                pleaseInterrupt.countDown();
+                try {
+                    q.offerLast(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * takeLast retrieves elements in FIFO order
+     */
+    public void testTakeLast() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i - 1, q.takeLast());
+        }
+    }
+
+    /**
+     * takeLast removes existing elements until empty, then blocks interruptibly
+     */
+    public void testBlockingTakeLast() throws InterruptedException {
+        final LinkedBlockingDeque q = populatedDeque(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(SIZE - i - 1, q.takeLast());
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.takeLast();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.takeLast();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * timed pollLast with zero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPollLast0() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i - 1, q.pollLast(0, MILLISECONDS));
+        }
+        assertNull(q.pollLast(0, MILLISECONDS));
+    }
+
+    /**
+     * timed pollLast with nonzero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPollLast() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            long startTime = System.nanoTime();
+            assertEquals(SIZE - i - 1, q.pollLast(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+        long startTime = System.nanoTime();
+        assertNull(q.pollLast(timeoutMillis(), MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        checkEmpty(q);
+    }
+
+    /**
+     * Interrupted timed pollLast throws InterruptedException instead of
+     * returning timeout status
+     */
+    public void testInterruptedTimedPollLast() throws InterruptedException {
+        final LinkedBlockingDeque q = populatedDeque(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(SIZE - i - 1,
+                                 q.pollLast(LONG_DELAY_MS, MILLISECONDS));
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.pollLast(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.pollLast(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        checkEmpty(q);
+    }
+
+    /**
+     * timed poll before a delayed offerLast fails; after offerLast succeeds;
+     * on interruption throws
+     */
+    public void testTimedPollWithOfferLast() throws InterruptedException {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(2);
+        final CheckedBarrier barrier = new CheckedBarrier(2);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+
+                barrier.await();
+
+                assertSame(zero, q.poll(LONG_DELAY_MS, MILLISECONDS));
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                barrier.await();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+            }});
+
+        barrier.await();
+        long startTime = System.nanoTime();
+        assertTrue(q.offerLast(zero, LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+
+        barrier.await();
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * element returns next element, or throws NSEE if empty
+     */
+    public void testElement() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.element());
+            q.poll();
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.poll();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        assertEquals(SIZE, q.remainingCapacity());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(one));
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        LinkedBlockingDeque p = new LinkedBlockingDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        LinkedBlockingDeque p = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            LinkedBlockingDeque q = populatedDeque(SIZE);
+            LinkedBlockingDeque p = populatedDeque(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer x = (Integer)(p.remove());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    /**
+     * toArray contains all elements in FIFO order
+     */
+    public void testToArray() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.poll());
+    }
+
+    /**
+     * toArray(a) contains all elements in FIFO order
+     */
+    public void testToArray2() {
+        LinkedBlockingDeque<Integer> q = populatedDeque(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.remove());
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray1_BadArg() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        try {
+            q.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        Iterator it = q.iterator();
+        int i;
+        for (i = 0; it.hasNext(); i++)
+            assertTrue(q.contains(it.next()));
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+
+        it = q.iterator();
+        for (i = 0; it.hasNext(); i++)
+            assertEquals(it.next(), q.take());
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty collection has no elements
+     */
+    public void testEmptyIterator() {
+        Deque c = new LinkedBlockingDeque();
+        assertIteratorExhausted(c.iterator());
+        assertIteratorExhausted(c.descendingIterator());
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(3);
+        q.add(two);
+        q.add(one);
+        q.add(three);
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertSame(it.next(), one);
+        assertSame(it.next(), three);
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * iterator ordering is FIFO
+     */
+    public void testIteratorOrdering() {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(3);
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        assertEquals(0, q.remainingCapacity());
+        int k = 0;
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            assertEquals(++k, it.next());
+        }
+        assertEquals(3, k);
+    }
+
+    /**
+     * Modifications do not cause iterators to fail
+     */
+    public void testWeaklyConsistentIteration() {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(3);
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            q.remove();
+            it.next();
+        }
+        assertEquals(0, q.size());
+    }
+
+    /**
+     * Descending iterator iterates through all elements
+     */
+    public void testDescendingIterator() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        int i = 0;
+        Iterator it = q.descendingIterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+        assertFalse(it.hasNext());
+        try {
+            it.next();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * Descending iterator ordering is reverse FIFO
+     */
+    public void testDescendingIteratorOrdering() {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque();
+        for (int iters = 0; iters < 100; ++iters) {
+            q.add(new Integer(3));
+            q.add(new Integer(2));
+            q.add(new Integer(1));
+            int k = 0;
+            for (Iterator it = q.descendingIterator(); it.hasNext();) {
+                assertEquals(++k, it.next());
+            }
+
+            assertEquals(3, k);
+            q.remove();
+            q.remove();
+            q.remove();
+        }
+    }
+
+    /**
+     * descendingIterator.remove removes current element
+     */
+    public void testDescendingIteratorRemove() {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque();
+        for (int iters = 0; iters < 100; ++iters) {
+            q.add(new Integer(3));
+            q.add(new Integer(2));
+            q.add(new Integer(1));
+            Iterator it = q.descendingIterator();
+            assertEquals(it.next(), new Integer(1));
+            it.remove();
+            assertEquals(it.next(), new Integer(2));
+            it = q.descendingIterator();
+            assertEquals(it.next(), new Integer(2));
+            assertEquals(it.next(), new Integer(3));
+            it.remove();
+            assertFalse(it.hasNext());
+            q.remove();
+        }
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * offer transfers elements across Executor tasks
+     */
+    public void testOfferInExecutor() {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(2);
+        q.add(one);
+        q.add(two);
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        final ExecutorService executor = Executors.newFixedThreadPool(2);
+        try (PoolCleaner cleaner = cleaner(executor)) {
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(q.offer(three));
+                    threadsStarted.await();
+                    assertTrue(q.offer(three, LONG_DELAY_MS, MILLISECONDS));
+                    assertEquals(0, q.remainingCapacity());
+                }});
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadsStarted.await();
+                    assertSame(one, q.take());
+                }});
+        }
+    }
+
+    /**
+     * timed poll retrieves elements across Executor threads
+     */
+    public void testPollInExecutor() {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(2);
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        final ExecutorService executor = Executors.newFixedThreadPool(2);
+        try (PoolCleaner cleaner = cleaner(executor)) {
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertNull(q.poll());
+                    threadsStarted.await();
+                    assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
+                    checkEmpty(q);
+                }});
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadsStarted.await();
+                    q.put(one);
+                }});
+        }
+    }
+
+    /**
+     * A deserialized serialized deque has same elements in same order
+     */
+    public void testSerialization() throws Exception {
+        Queue x = populatedDeque(SIZE);
+        Queue y = serialClone(x);
+
+        assertNotSame(y, x);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertTrue(Arrays.equals(x.toArray(), y.toArray()));
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(), y.remove());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * drainTo(c) empties deque into another collection c
+     */
+    public void testDrainTo() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(SIZE, l.size());
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(l.get(i), new Integer(i));
+        q.add(zero);
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(zero));
+        assertTrue(q.contains(one));
+        l.clear();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(2, l.size());
+        for (int i = 0; i < 2; ++i)
+            assertEquals(l.get(i), new Integer(i));
+    }
+
+    /**
+     * drainTo empties full deque, unblocking a waiting put.
+     */
+    public void testDrainToWithActivePut() throws InterruptedException {
+        final LinkedBlockingDeque q = populatedDeque(SIZE);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(new Integer(SIZE + 1));
+            }});
+
+        t.start();
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertTrue(l.size() >= SIZE);
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(l.get(i), new Integer(i));
+        t.join();
+        assertTrue(q.size() + l.size() >= SIZE);
+    }
+
+    /**
+     * drainTo(c, n) empties first min(n, size) elements of queue into c
+     */
+    public void testDrainToN() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque();
+        for (int i = 0; i < SIZE + 2; ++i) {
+            for (int j = 0; j < SIZE; j++)
+                assertTrue(q.offer(new Integer(j)));
+            ArrayList l = new ArrayList();
+            q.drainTo(l, i);
+            int k = (i < SIZE) ? i : SIZE;
+            assertEquals(k, l.size());
+            assertEquals(SIZE - k, q.size());
+            for (int j = 0; j < k; ++j)
+                assertEquals(l.get(j), new Integer(j));
+            do {} while (q.poll() != null);
+        }
+    }
+
+    /**
+     * remove(null), contains(null) always return false
+     */
+    public void testNeverContainsNull() {
+        Deque<?>[] qs = {
+            new LinkedBlockingDeque<Object>(),
+            populatedDeque(2),
+        };
+
+        for (Deque<?> q : qs) {
+            assertFalse(q.contains(null));
+            assertFalse(q.remove(null));
+            assertFalse(q.removeFirstOccurrence(null));
+            assertFalse(q.removeLastOccurrence(null));
+        }
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/LinkedBlockingQueueTest.java b/jdk/test/java/util/concurrent/tck/LinkedBlockingQueueTest.java
new file mode 100644
index 00000000000..51246e903a6
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/LinkedBlockingQueueTest.java
@@ -0,0 +1,889 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import junit.framework.Test;
+
+public class LinkedBlockingQueueTest extends JSR166TestCase {
+
+    public static class Unbounded extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new LinkedBlockingQueue();
+        }
+    }
+
+    public static class Bounded extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new LinkedBlockingQueue(SIZE);
+        }
+    }
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return newTestSuite(LinkedBlockingQueueTest.class,
+                            new Unbounded().testSuite(),
+                            new Bounded().testSuite());
+    }
+
+    /**
+     * Returns a new queue of given size containing consecutive
+     * Integers 0 ... n.
+     */
+    private LinkedBlockingQueue<Integer> populatedQueue(int n) {
+        LinkedBlockingQueue<Integer> q =
+            new LinkedBlockingQueue<Integer>(n);
+        assertTrue(q.isEmpty());
+        for (int i = 0; i < n; i++)
+            assertTrue(q.offer(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(0, q.remainingCapacity());
+        assertEquals(n, q.size());
+        return q;
+    }
+
+    /**
+     * A new queue has the indicated capacity, or Integer.MAX_VALUE if
+     * none given
+     */
+    public void testConstructor1() {
+        assertEquals(SIZE, new LinkedBlockingQueue(SIZE).remainingCapacity());
+        assertEquals(Integer.MAX_VALUE, new LinkedBlockingQueue().remainingCapacity());
+    }
+
+    /**
+     * Constructor throws IllegalArgumentException if capacity argument nonpositive
+     */
+    public void testConstructor2() {
+        try {
+            new LinkedBlockingQueue(0);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Initializing from null Collection throws NullPointerException
+     */
+    public void testConstructor3() {
+        try {
+            new LinkedBlockingQueue(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NullPointerException
+     */
+    public void testConstructor4() {
+        Collection<Integer> elements = Arrays.asList(new Integer[SIZE]);
+        try {
+            new LinkedBlockingQueue(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws
+     * NullPointerException
+     */
+    public void testConstructor5() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        Collection<Integer> elements = Arrays.asList(ints);
+        try {
+            new LinkedBlockingQueue(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        LinkedBlockingQueue q = new LinkedBlockingQueue(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * Queue transitions from empty to full when elements added
+     */
+    public void testEmptyFull() {
+        LinkedBlockingQueue q = new LinkedBlockingQueue(2);
+        assertTrue(q.isEmpty());
+        assertEquals("should have room for 2", 2, q.remainingCapacity());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        q.add(two);
+        assertFalse(q.isEmpty());
+        assertEquals(0, q.remainingCapacity());
+        assertFalse(q.offer(three));
+    }
+
+    /**
+     * remainingCapacity decreases on add, increases on remove
+     */
+    public void testRemainingCapacity() {
+        BlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remainingCapacity());
+            assertEquals(SIZE, q.size() + q.remainingCapacity());
+            assertEquals(i, q.remove());
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i, q.remainingCapacity());
+            assertEquals(SIZE, q.size() + q.remainingCapacity());
+            assertTrue(q.add(i));
+        }
+    }
+
+    /**
+     * Offer succeeds if not full; fails if full
+     */
+    public void testOffer() {
+        LinkedBlockingQueue q = new LinkedBlockingQueue(1);
+        assertTrue(q.offer(zero));
+        assertFalse(q.offer(one));
+    }
+
+    /**
+     * add succeeds if not full; throws IllegalStateException if full
+     */
+    public void testAdd() {
+        LinkedBlockingQueue q = new LinkedBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i)
+            assertTrue(q.add(new Integer(i)));
+        assertEquals(0, q.remainingCapacity());
+        try {
+            q.add(new Integer(SIZE));
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * addAll(this) throws IllegalArgumentException
+     */
+    public void testAddAllSelf() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        try {
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        LinkedBlockingQueue q = new LinkedBlockingQueue(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        Collection<Integer> elements = Arrays.asList(ints);
+        try {
+            q.addAll(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll throws IllegalStateException if not enough room
+     */
+    public void testAddAll4() {
+        LinkedBlockingQueue q = new LinkedBlockingQueue(SIZE - 1);
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        Collection<Integer> elements = Arrays.asList(ints);
+        try {
+            q.addAll(elements);
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * Queue contains all elements, in traversal order, of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        LinkedBlockingQueue q = new LinkedBlockingQueue(SIZE);
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * all elements successfully put are contained
+     */
+    public void testPut() throws InterruptedException {
+        LinkedBlockingQueue q = new LinkedBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            Integer x = new Integer(i);
+            q.put(x);
+            assertTrue(q.contains(x));
+        }
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * put blocks interruptibly if full
+     */
+    public void testBlockingPut() throws InterruptedException {
+        final LinkedBlockingQueue q = new LinkedBlockingQueue(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i)
+                    q.put(i);
+                assertEquals(SIZE, q.size());
+                assertEquals(0, q.remainingCapacity());
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(SIZE, q.size());
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * put blocks interruptibly waiting for take when full
+     */
+    public void testPutWithTake() throws InterruptedException {
+        final int capacity = 2;
+        final LinkedBlockingQueue q = new LinkedBlockingQueue(2);
+        final CountDownLatch pleaseTake = new CountDownLatch(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < capacity; i++)
+                    q.put(i);
+                pleaseTake.countDown();
+                q.put(86);
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseTake);
+        assertEquals(0, q.remainingCapacity());
+        assertEquals(0, q.take());
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * timed offer times out if full and elements not taken
+     */
+    public void testTimedOffer() {
+        final LinkedBlockingQueue q = new LinkedBlockingQueue(2);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(new Object());
+                q.put(new Object());
+                long startTime = System.nanoTime();
+                assertFalse(q.offer(new Object(), timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                pleaseInterrupt.countDown();
+                try {
+                    q.offer(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * take retrieves elements in FIFO order
+     */
+    public void testTake() throws InterruptedException {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.take());
+        }
+    }
+
+    /**
+     * Take removes existing elements until empty, then blocks interruptibly
+     */
+    public void testBlockingTake() throws InterruptedException {
+        final BlockingQueue q = populatedQueue(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(i, q.take());
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll());
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * timed poll with zero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll0() throws InterruptedException {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll(0, MILLISECONDS));
+        }
+        assertNull(q.poll(0, MILLISECONDS));
+    }
+
+    /**
+     * timed poll with nonzero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll() throws InterruptedException {
+        LinkedBlockingQueue<Integer> q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            long startTime = System.nanoTime();
+            assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+        long startTime = System.nanoTime();
+        assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        checkEmpty(q);
+    }
+
+    /**
+     * Interrupted timed poll throws InterruptedException instead of
+     * returning timeout status
+     */
+    public void testInterruptedTimedPoll() throws InterruptedException {
+        final BlockingQueue<Integer> q = populatedQueue(SIZE);
+        final CountDownLatch aboutToWait = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
+                }
+                aboutToWait.countDown();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {
+                    assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+                }
+            }});
+
+        await(aboutToWait);
+        waitForThreadToEnterWaitState(t, LONG_DELAY_MS);
+        t.interrupt();
+        awaitTermination(t);
+        checkEmpty(q);
+    }
+
+    /**
+     * peek returns next element, or null if empty
+     */
+    public void testPeek() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peek());
+            assertEquals(i, q.poll());
+            assertTrue(q.peek() == null ||
+                       !q.peek().equals(i));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * element returns next element, or throws NSEE if empty
+     */
+    public void testElement() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.element());
+            assertEquals(i, q.poll());
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * An add following remove(x) succeeds
+     */
+    public void testRemoveElementAndAdd() throws InterruptedException {
+        LinkedBlockingQueue q = new LinkedBlockingQueue();
+        assertTrue(q.add(new Integer(1)));
+        assertTrue(q.add(new Integer(2)));
+        assertTrue(q.remove(new Integer(1)));
+        assertTrue(q.remove(new Integer(2)));
+        assertTrue(q.add(new Integer(3)));
+        assertNotNull(q.take());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.poll();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        assertEquals(SIZE, q.remainingCapacity());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(one));
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        LinkedBlockingQueue p = new LinkedBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        LinkedBlockingQueue p = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            LinkedBlockingQueue q = populatedQueue(SIZE);
+            LinkedBlockingQueue p = populatedQueue(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer x = (Integer)(p.remove());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    /**
+     * toArray contains all elements in FIFO order
+     */
+    public void testToArray() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.poll());
+    }
+
+    /**
+     * toArray(a) contains all elements in FIFO order
+     */
+    public void testToArray2() throws InterruptedException {
+        LinkedBlockingQueue<Integer> q = populatedQueue(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.poll());
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray1_BadArg() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        try {
+            q.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() throws InterruptedException {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        Iterator it = q.iterator();
+        int i;
+        for (i = 0; it.hasNext(); i++)
+            assertTrue(q.contains(it.next()));
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+
+        it = q.iterator();
+        for (i = 0; it.hasNext(); i++)
+            assertEquals(it.next(), q.take());
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty collection has no elements
+     */
+    public void testEmptyIterator() {
+        assertIteratorExhausted(new LinkedBlockingQueue().iterator());
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final LinkedBlockingQueue q = new LinkedBlockingQueue(3);
+        q.add(two);
+        q.add(one);
+        q.add(three);
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertSame(it.next(), one);
+        assertSame(it.next(), three);
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * iterator ordering is FIFO
+     */
+    public void testIteratorOrdering() {
+        final LinkedBlockingQueue q = new LinkedBlockingQueue(3);
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        assertEquals(0, q.remainingCapacity());
+        int k = 0;
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            assertEquals(++k, it.next());
+        }
+        assertEquals(3, k);
+    }
+
+    /**
+     * Modifications do not cause iterators to fail
+     */
+    public void testWeaklyConsistentIteration() {
+        final LinkedBlockingQueue q = new LinkedBlockingQueue(3);
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            q.remove();
+            it.next();
+        }
+        assertEquals(0, q.size());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * offer transfers elements across Executor tasks
+     */
+    public void testOfferInExecutor() {
+        final LinkedBlockingQueue q = new LinkedBlockingQueue(2);
+        q.add(one);
+        q.add(two);
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        final ExecutorService executor = Executors.newFixedThreadPool(2);
+        try (PoolCleaner cleaner = cleaner(executor)) {
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(q.offer(three));
+                    threadsStarted.await();
+                    assertTrue(q.offer(three, LONG_DELAY_MS, MILLISECONDS));
+                    assertEquals(0, q.remainingCapacity());
+                }});
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadsStarted.await();
+                    assertSame(one, q.take());
+                }});
+        }
+    }
+
+    /**
+     * timed poll retrieves elements across Executor threads
+     */
+    public void testPollInExecutor() {
+        final LinkedBlockingQueue q = new LinkedBlockingQueue(2);
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        final ExecutorService executor = Executors.newFixedThreadPool(2);
+        try (PoolCleaner cleaner = cleaner(executor)) {
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertNull(q.poll());
+                    threadsStarted.await();
+                    assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
+                    checkEmpty(q);
+                }});
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadsStarted.await();
+                    q.put(one);
+                }});
+        }
+    }
+
+    /**
+     * A deserialized serialized queue has same elements in same order
+     */
+    public void testSerialization() throws Exception {
+        Queue x = populatedQueue(SIZE);
+        Queue y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertTrue(Arrays.equals(x.toArray(), y.toArray()));
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(), y.remove());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * drainTo(c) empties queue into another collection c
+     */
+    public void testDrainTo() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(SIZE, l.size());
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(l.get(i), new Integer(i));
+        q.add(zero);
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(zero));
+        assertTrue(q.contains(one));
+        l.clear();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(2, l.size());
+        for (int i = 0; i < 2; ++i)
+            assertEquals(l.get(i), new Integer(i));
+    }
+
+    /**
+     * drainTo empties full queue, unblocking a waiting put.
+     */
+    public void testDrainToWithActivePut() throws InterruptedException {
+        final LinkedBlockingQueue q = populatedQueue(SIZE);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(new Integer(SIZE + 1));
+            }});
+
+        t.start();
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertTrue(l.size() >= SIZE);
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(l.get(i), new Integer(i));
+        t.join();
+        assertTrue(q.size() + l.size() >= SIZE);
+    }
+
+    /**
+     * drainTo(c, n) empties first min(n, size) elements of queue into c
+     */
+    public void testDrainToN() {
+        LinkedBlockingQueue q = new LinkedBlockingQueue();
+        for (int i = 0; i < SIZE + 2; ++i) {
+            for (int j = 0; j < SIZE; j++)
+                assertTrue(q.offer(new Integer(j)));
+            ArrayList l = new ArrayList();
+            q.drainTo(l, i);
+            int k = (i < SIZE) ? i : SIZE;
+            assertEquals(k, l.size());
+            assertEquals(SIZE - k, q.size());
+            for (int j = 0; j < k; ++j)
+                assertEquals(l.get(j), new Integer(j));
+            do {} while (q.poll() != null);
+        }
+    }
+
+    /**
+     * remove(null), contains(null) always return false
+     */
+    public void testNeverContainsNull() {
+        Collection<?>[] qs = {
+            new LinkedBlockingQueue<Object>(),
+            populatedQueue(2),
+        };
+
+        for (Collection<?> q : qs) {
+            assertFalse(q.contains(null));
+            assertFalse(q.remove(null));
+        }
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/LinkedListTest.java b/jdk/test/java/util/concurrent/tck/LinkedListTest.java
new file mode 100644
index 00000000000..ea0f689dc70
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/LinkedListTest.java
@@ -0,0 +1,669 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.NoSuchElementException;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class LinkedListTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(LinkedListTest.class);
+    }
+
+    /**
+     * Returns a new queue of given size containing consecutive
+     * Integers 0 ... n.
+     */
+    private LinkedList<Integer> populatedQueue(int n) {
+        LinkedList<Integer> q = new LinkedList<Integer>();
+        assertTrue(q.isEmpty());
+        for (int i = 0; i < n; ++i)
+            assertTrue(q.offer(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(n, q.size());
+        return q;
+    }
+
+    /**
+     * new queue is empty
+     */
+    public void testConstructor1() {
+        assertEquals(0, new LinkedList().size());
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            new LinkedList((Collection)null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = i;
+        LinkedList q = new LinkedList(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        LinkedList q = new LinkedList();
+        assertTrue(q.isEmpty());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.add(new Integer(2));
+        q.remove();
+        q.remove();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testSize() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i, q.size());
+            q.remove();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * offer(null) succeeds
+     */
+    public void testOfferNull() {
+        LinkedList q = new LinkedList();
+        q.offer(null);
+        assertNull(q.get(0));
+        assertTrue(q.contains(null));
+    }
+
+    /**
+     * Offer succeeds
+     */
+    public void testOffer() {
+        LinkedList q = new LinkedList();
+        assertTrue(q.offer(new Integer(0)));
+        assertTrue(q.offer(new Integer(1)));
+    }
+
+    /**
+     * add succeeds
+     */
+    public void testAdd() {
+        LinkedList q = new LinkedList();
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            assertTrue(q.add(new Integer(i)));
+        }
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        LinkedList q = new LinkedList();
+        try {
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements, in traversal order, of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = i;
+        LinkedList q = new LinkedList();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * addAll with too large an index throws IOOBE
+     */
+    public void testAddAll2_IndexOutOfBoundsException() {
+        LinkedList l = new LinkedList();
+        l.add(new Object());
+        LinkedList m = new LinkedList();
+        m.add(new Object());
+        try {
+            l.addAll(4,m);
+            shouldThrow();
+        } catch (IndexOutOfBoundsException success) {}
+    }
+
+    /**
+     * addAll with negative index throws IOOBE
+     */
+    public void testAddAll4_BadIndex() {
+        LinkedList l = new LinkedList();
+        l.add(new Object());
+        LinkedList m = new LinkedList();
+        m.add(new Object());
+        try {
+            l.addAll(-1,m);
+            shouldThrow();
+        } catch (IndexOutOfBoundsException success) {}
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll());
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * peek returns next element, or null if empty
+     */
+    public void testPeek() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peek());
+            assertEquals(i, q.poll());
+            assertTrue(q.peek() == null ||
+                       !q.peek().equals(i));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * element returns next element, or throws NSEE if empty
+     */
+    public void testElement() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.element());
+            assertEquals(i, q.poll());
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testRemoveElement() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove((Integer)i));
+            assertFalse(q.contains(i));
+            assertTrue(q.contains(i - 1));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove((Integer)i));
+            assertFalse(q.contains(i));
+            assertFalse(q.remove((Integer)(i + 1)));
+            assertFalse(q.contains(i + 1));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.poll();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        LinkedList q = populatedQueue(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        assertTrue(q.add(new Integer(1)));
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        LinkedList q = populatedQueue(SIZE);
+        LinkedList p = new LinkedList();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            assertTrue(p.add(new Integer(i)));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        LinkedList q = populatedQueue(SIZE);
+        LinkedList p = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            LinkedList q = populatedQueue(SIZE);
+            LinkedList p = populatedQueue(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer x = (Integer)(p.remove());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    /**
+     * toArray contains all elements in FIFO order
+     */
+    public void testToArray() {
+        LinkedList q = populatedQueue(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.poll());
+    }
+
+    /**
+     * toArray(a) contains all elements in FIFO order
+     */
+    public void testToArray2() {
+        LinkedList<Integer> q = populatedQueue(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.poll());
+    }
+
+    /**
+     * toArray(null) throws NullPointerException
+     */
+    public void testToArray_NullArg() {
+        LinkedList l = new LinkedList();
+        l.add(new Object());
+        try {
+            l.toArray(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray1_BadArg() {
+        LinkedList l = new LinkedList();
+        l.add(new Integer(5));
+        try {
+            l.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() {
+        LinkedList q = populatedQueue(SIZE);
+        Iterator it = q.iterator();
+        int i;
+        for (i = 0; it.hasNext(); i++)
+            assertTrue(q.contains(it.next()));
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty collection has no elements
+     */
+    public void testEmptyIterator() {
+        assertIteratorExhausted(new LinkedList().iterator());
+    }
+
+    /**
+     * iterator ordering is FIFO
+     */
+    public void testIteratorOrdering() {
+        final LinkedList q = new LinkedList();
+        q.add(new Integer(1));
+        q.add(new Integer(2));
+        q.add(new Integer(3));
+        int k = 0;
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            assertEquals(++k, it.next());
+        }
+
+        assertEquals(3, k);
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final LinkedList q = new LinkedList();
+        q.add(new Integer(1));
+        q.add(new Integer(2));
+        q.add(new Integer(3));
+        Iterator it = q.iterator();
+        assertEquals(1, it.next());
+        it.remove();
+        it = q.iterator();
+        assertEquals(2, it.next());
+        assertEquals(3, it.next());
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * Descending iterator iterates through all elements
+     */
+    public void testDescendingIterator() {
+        LinkedList q = populatedQueue(SIZE);
+        int i = 0;
+        Iterator it = q.descendingIterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+        assertFalse(it.hasNext());
+        try {
+            it.next();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * Descending iterator ordering is reverse FIFO
+     */
+    public void testDescendingIteratorOrdering() {
+        final LinkedList q = new LinkedList();
+        q.add(new Integer(3));
+        q.add(new Integer(2));
+        q.add(new Integer(1));
+        int k = 0;
+        for (Iterator it = q.descendingIterator(); it.hasNext();) {
+            assertEquals(++k, it.next());
+        }
+
+        assertEquals(3, k);
+    }
+
+    /**
+     * descendingIterator.remove removes current element
+     */
+    public void testDescendingIteratorRemove() {
+        final LinkedList q = new LinkedList();
+        q.add(three);
+        q.add(two);
+        q.add(one);
+        Iterator it = q.descendingIterator();
+        it.next();
+        it.remove();
+        it = q.descendingIterator();
+        assertSame(it.next(), two);
+        assertSame(it.next(), three);
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        LinkedList q = populatedQueue(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * peek returns element inserted with addFirst
+     */
+    public void testAddFirst() {
+        LinkedList q = populatedQueue(3);
+        q.addFirst(four);
+        assertSame(four, q.peek());
+    }
+
+    /**
+     * peekFirst returns element inserted with push
+     */
+    public void testPush() {
+        LinkedList q = populatedQueue(3);
+        q.push(four);
+        assertSame(four, q.peekFirst());
+    }
+
+    /**
+     * pop removes next element, or throws NSEE if empty
+     */
+    public void testPop() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pop());
+        }
+        try {
+            q.pop();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * OfferFirst succeeds
+     */
+    public void testOfferFirst() {
+        LinkedList q = new LinkedList();
+        assertTrue(q.offerFirst(new Integer(0)));
+        assertTrue(q.offerFirst(new Integer(1)));
+    }
+
+    /**
+     * OfferLast succeeds
+     */
+    public void testOfferLast() {
+        LinkedList q = new LinkedList();
+        assertTrue(q.offerLast(new Integer(0)));
+        assertTrue(q.offerLast(new Integer(1)));
+    }
+
+    /**
+     * pollLast succeeds unless empty
+     */
+    public void testPollLast() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.pollLast());
+        }
+        assertNull(q.pollLast());
+    }
+
+    /**
+     * peekFirst returns next element, or null if empty
+     */
+    public void testPeekFirst() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peekFirst());
+            assertEquals(i, q.pollFirst());
+            assertTrue(q.peekFirst() == null ||
+                       !q.peekFirst().equals(i));
+        }
+        assertNull(q.peekFirst());
+    }
+
+    /**
+     * peekLast returns next element, or null if empty
+     */
+    public void testPeekLast() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.peekLast());
+            assertEquals(i, q.pollLast());
+            assertTrue(q.peekLast() == null ||
+                       !q.peekLast().equals(i));
+        }
+        assertNull(q.peekLast());
+    }
+
+    public void testFirstElement() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.getFirst());
+            assertEquals(i, q.pollFirst());
+        }
+        try {
+            q.getFirst();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * getLast returns next element, or throws NSEE if empty
+     */
+    public void testLastElement() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.getLast());
+            assertEquals(i, q.pollLast());
+        }
+        try {
+            q.getLast();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * removeFirstOccurrence(x) removes x and returns true if present
+     */
+    public void testRemoveFirstOccurrence() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.removeFirstOccurrence(new Integer(i)));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.removeFirstOccurrence(new Integer(i)));
+            assertFalse(q.removeFirstOccurrence(new Integer(i + 1)));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * removeLastOccurrence(x) removes x and returns true if present
+     */
+    public void testRemoveLastOccurrence() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.removeLastOccurrence(new Integer(i)));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.removeLastOccurrence(new Integer(i)));
+            assertFalse(q.removeLastOccurrence(new Integer(i + 1)));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/LinkedTransferQueueTest.java b/jdk/test/java/util/concurrent/tck/LinkedTransferQueueTest.java
new file mode 100644
index 00000000000..dd57870a306
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/LinkedTransferQueueTest.java
@@ -0,0 +1,1085 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include John Vint
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedTransferQueue;
+
+import junit.framework.Test;
+
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class LinkedTransferQueueTest extends JSR166TestCase {
+    static class Implementation implements CollectionImplementation {
+        public Class<?> klazz() { return LinkedTransferQueue.class; }
+        public Collection emptyCollection() { return new LinkedTransferQueue(); }
+        public Object makeElement(int i) { return i; }
+        public boolean isConcurrent() { return true; }
+        public boolean permitsNulls() { return false; }
+    }
+
+    public static class Generic extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new LinkedTransferQueue();
+        }
+    }
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return newTestSuite(LinkedTransferQueueTest.class,
+                            new Generic().testSuite(),
+                            CollectionTest.testSuite(new Implementation()));
+    }
+
+    /**
+     * Constructor builds new queue with size being zero and empty
+     * being true
+     */
+    public void testConstructor1() {
+        assertEquals(0, new LinkedTransferQueue().size());
+        assertTrue(new LinkedTransferQueue().isEmpty());
+    }
+
+    /**
+     * Initializing constructor with null collection throws
+     * NullPointerException
+     */
+    public void testConstructor2() {
+        try {
+            new LinkedTransferQueue(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws
+     * NullPointerException
+     */
+    public void testConstructor3() {
+        Collection<Integer> elements = Arrays.asList(new Integer[SIZE]);
+        try {
+            new LinkedTransferQueue(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing constructor with a collection containing some null elements
+     * throws NullPointerException
+     */
+    public void testConstructor4() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = i;
+        Collection<Integer> elements = Arrays.asList(ints);
+        try {
+            new LinkedTransferQueue(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of the collection it is initialized by
+     */
+    public void testConstructor5() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i) {
+            ints[i] = i;
+        }
+        List intList = Arrays.asList(ints);
+        LinkedTransferQueue q
+            = new LinkedTransferQueue(intList);
+        assertEquals(q.size(), intList.size());
+        assertEquals(q.toString(), intList.toString());
+        assertTrue(Arrays.equals(q.toArray(),
+                                     intList.toArray()));
+        assertTrue(Arrays.equals(q.toArray(new Object[0]),
+                                 intList.toArray(new Object[0])));
+        assertTrue(Arrays.equals(q.toArray(new Object[SIZE]),
+                                 intList.toArray(new Object[SIZE])));
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(ints[i], q.poll());
+        }
+    }
+
+    /**
+     * remainingCapacity() always returns Integer.MAX_VALUE
+     */
+    public void testRemainingCapacity() {
+        BlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+            assertEquals(SIZE - i, q.size());
+            assertEquals(i, q.remove());
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+            assertEquals(i, q.size());
+            assertTrue(q.add(i));
+        }
+    }
+
+    /**
+     * addAll(this) throws IllegalArgumentException
+     */
+    public void testAddAllSelf() {
+        LinkedTransferQueue q = populatedQueue(SIZE);
+        try {
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws
+     * NullPointerException after possibly adding some elements
+     */
+    public void testAddAll3() {
+        LinkedTransferQueue q = new LinkedTransferQueue();
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = i;
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements, in traversal order, of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i) {
+            ints[i] = i;
+        }
+        LinkedTransferQueue q = new LinkedTransferQueue();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(ints[i], q.poll());
+        }
+    }
+
+    /**
+     * all elements successfully put are contained
+     */
+    public void testPut() {
+        LinkedTransferQueue<Integer> q = new LinkedTransferQueue<Integer>();
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.put(i);
+            assertTrue(q.contains(i));
+        }
+    }
+
+    /**
+     * take retrieves elements in FIFO order
+     */
+    public void testTake() throws InterruptedException {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, (int) q.take());
+        }
+    }
+
+    /**
+     * take removes existing elements until empty, then blocks interruptibly
+     */
+    public void testBlockingTake() throws InterruptedException {
+        final BlockingQueue q = populatedQueue(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(i, q.take());
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() throws InterruptedException {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, (int) q.poll());
+        }
+        assertNull(q.poll());
+        checkEmpty(q);
+    }
+
+    /**
+     * timed poll with zero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll0() throws InterruptedException {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, (int) q.poll(0, MILLISECONDS));
+        }
+        assertNull(q.poll(0, MILLISECONDS));
+        checkEmpty(q);
+    }
+
+    /**
+     * timed poll with nonzero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll() throws InterruptedException {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        long startTime = System.nanoTime();
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+
+        startTime = System.nanoTime();
+        assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        checkEmpty(q);
+    }
+
+    /**
+     * Interrupted timed poll throws InterruptedException instead of
+     * returning timeout status
+     */
+    public void testInterruptedTimedPoll() throws InterruptedException {
+        final BlockingQueue<Integer> q = populatedQueue(SIZE);
+        final CountDownLatch aboutToWait = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                for (int i = 0; i < SIZE; ++i)
+                    assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
+                aboutToWait.countDown();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+            }});
+
+        aboutToWait.await();
+        waitForThreadToEnterWaitState(t);
+        t.interrupt();
+        awaitTermination(t);
+        checkEmpty(q);
+    }
+
+    /**
+     * timed poll after thread interrupted throws InterruptedException
+     * instead of returning timeout status
+     */
+    public void testTimedPollAfterInterrupt() throws InterruptedException {
+        final BlockingQueue<Integer> q = populatedQueue(SIZE);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                Thread.currentThread().interrupt();
+                for (int i = 0; i < SIZE; ++i)
+                    assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+            }});
+
+        awaitTermination(t);
+        checkEmpty(q);
+    }
+
+    /**
+     * peek returns next element, or null if empty
+     */
+    public void testPeek() throws InterruptedException {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, (int) q.peek());
+            assertEquals(i, (int) q.poll());
+            assertTrue(q.peek() == null ||
+                       i != (int) q.peek());
+        }
+        assertNull(q.peek());
+        checkEmpty(q);
+    }
+
+    /**
+     * element returns next element, or throws NoSuchElementException if empty
+     */
+    public void testElement() throws InterruptedException {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, (int) q.element());
+            assertEquals(i, (int) q.poll());
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        checkEmpty(q);
+    }
+
+    /**
+     * remove removes next element, or throws NoSuchElementException if empty
+     */
+    public void testRemove() throws InterruptedException {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, (int) q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        checkEmpty(q);
+    }
+
+    /**
+     * An add following remove(x) succeeds
+     */
+    public void testRemoveElementAndAdd() throws InterruptedException {
+        LinkedTransferQueue q = new LinkedTransferQueue();
+        assertTrue(q.add(one));
+        assertTrue(q.add(two));
+        assertTrue(q.remove(one));
+        assertTrue(q.remove(two));
+        assertTrue(q.add(three));
+        assertSame(q.take(), three);
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(i));
+            assertEquals(i, (int) q.poll());
+            assertFalse(q.contains(i));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() throws InterruptedException {
+        LinkedTransferQueue q = populatedQueue(SIZE);
+        q.clear();
+        checkEmpty(q);
+        assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertEquals(1, q.size());
+        assertTrue(q.contains(one));
+        q.clear();
+        checkEmpty(q);
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        LinkedTransferQueue<Integer> p = new LinkedTransferQueue<Integer>();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(i);
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true
+     * if changed
+     */
+    public void testRetainAll() {
+        LinkedTransferQueue q = populatedQueue(SIZE);
+        LinkedTransferQueue p = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0) {
+                assertFalse(changed);
+            } else {
+                assertTrue(changed);
+            }
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true
+     * if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            LinkedTransferQueue q = populatedQueue(SIZE);
+            LinkedTransferQueue p = populatedQueue(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                assertFalse(q.contains(p.remove()));
+            }
+        }
+    }
+
+    /**
+     * toArray() contains all elements in FIFO order
+     */
+    public void testToArray() {
+        LinkedTransferQueue q = populatedQueue(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++) {
+            assertSame(o[i], q.poll());
+        }
+    }
+
+    /**
+     * toArray(a) contains all elements in FIFO order
+     */
+    public void testToArray2() {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        for (int i = 0; i < ints.length; i++) {
+            assertSame(ints[i], q.poll());
+        }
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray1_BadArg() {
+        LinkedTransferQueue q = populatedQueue(SIZE);
+        try {
+            q.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() throws InterruptedException {
+        LinkedTransferQueue q = populatedQueue(SIZE);
+        Iterator it = q.iterator();
+        int i;
+        for (i = 0; it.hasNext(); i++)
+            assertTrue(q.contains(it.next()));
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+
+        it = q.iterator();
+        for (i = 0; it.hasNext(); i++)
+            assertEquals(it.next(), q.take());
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty collection has no elements
+     */
+    public void testEmptyIterator() {
+        assertIteratorExhausted(new LinkedTransferQueue().iterator());
+    }
+
+    /**
+     * iterator.remove() removes current element
+     */
+    public void testIteratorRemove() {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+        q.add(two);
+        q.add(one);
+        q.add(three);
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertSame(it.next(), one);
+        assertSame(it.next(), three);
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * iterator ordering is FIFO
+     */
+    public void testIteratorOrdering() {
+        final LinkedTransferQueue<Integer> q
+            = new LinkedTransferQueue<Integer>();
+        assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+        int k = 0;
+        for (Integer n : q) {
+            assertEquals(++k, (int) n);
+        }
+        assertEquals(3, k);
+    }
+
+    /**
+     * Modifications do not cause iterators to fail
+     */
+    public void testWeaklyConsistentIteration() {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            q.remove();
+            it.next();
+        }
+        assertEquals(0, q.size());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        LinkedTransferQueue q = populatedQueue(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * offer transfers elements across Executor tasks
+     */
+    public void testOfferInExecutor() {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        final ExecutorService executor = Executors.newFixedThreadPool(2);
+        try (PoolCleaner cleaner = cleaner(executor)) {
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadsStarted.await();
+                    long startTime = System.nanoTime();
+                    assertTrue(q.offer(one, LONG_DELAY_MS, MILLISECONDS));
+                    assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+                }});
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadsStarted.await();
+                    assertSame(one, q.take());
+                    checkEmpty(q);
+                }});
+        }
+    }
+
+    /**
+     * timed poll retrieves elements across Executor threads
+     */
+    public void testPollInExecutor() {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        final ExecutorService executor = Executors.newFixedThreadPool(2);
+        try (PoolCleaner cleaner = cleaner(executor)) {
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertNull(q.poll());
+                    threadsStarted.await();
+                    long startTime = System.nanoTime();
+                    assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
+                    assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+                    checkEmpty(q);
+                }});
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadsStarted.await();
+                    q.put(one);
+                }});
+        }
+    }
+
+    /**
+     * A deserialized serialized queue has same elements in same order
+     */
+    public void testSerialization() throws Exception {
+        Queue x = populatedQueue(SIZE);
+        Queue y = serialClone(x);
+
+        assertNotSame(y, x);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertTrue(Arrays.equals(x.toArray(), y.toArray()));
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(), y.remove());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * drainTo(c) empties queue into another collection c
+     */
+    public void testDrainTo() {
+        LinkedTransferQueue q = populatedQueue(SIZE);
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(SIZE, l.size());
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, l.get(i));
+        }
+        q.add(zero);
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(zero));
+        assertTrue(q.contains(one));
+        l.clear();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(2, l.size());
+        for (int i = 0; i < 2; ++i) {
+            assertEquals(i, l.get(i));
+        }
+    }
+
+    /**
+     * drainTo(c) empties full queue, unblocking a waiting put.
+     */
+    public void testDrainToWithActivePut() throws InterruptedException {
+        final LinkedTransferQueue q = populatedQueue(SIZE);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                q.put(SIZE + 1);
+            }});
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertTrue(l.size() >= SIZE);
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(i, l.get(i));
+        awaitTermination(t);
+        assertTrue(q.size() + l.size() >= SIZE);
+    }
+
+    /**
+     * drainTo(c, n) empties first min(n, size) elements of queue into c
+     */
+    public void testDrainToN() {
+        LinkedTransferQueue q = new LinkedTransferQueue();
+        for (int i = 0; i < SIZE + 2; ++i) {
+            for (int j = 0; j < SIZE; j++) {
+                assertTrue(q.offer(j));
+            }
+            ArrayList l = new ArrayList();
+            q.drainTo(l, i);
+            int k = (i < SIZE) ? i : SIZE;
+            assertEquals(k, l.size());
+            assertEquals(SIZE - k, q.size());
+            for (int j = 0; j < k; ++j)
+                assertEquals(j, l.get(j));
+            do {} while (q.poll() != null);
+        }
+    }
+
+    /**
+     * timed poll() or take() increments the waiting consumer count;
+     * offer(e) decrements the waiting consumer count
+     */
+    public void testWaitingConsumer() throws InterruptedException {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+        assertEquals(0, q.getWaitingConsumerCount());
+        assertFalse(q.hasWaitingConsumer());
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                threadStarted.countDown();
+                long startTime = System.nanoTime();
+                assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
+                assertEquals(0, q.getWaitingConsumerCount());
+                assertFalse(q.hasWaitingConsumer());
+                assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+            }});
+
+        threadStarted.await();
+        waitForThreadToEnterWaitState(t);
+        assertEquals(1, q.getWaitingConsumerCount());
+        assertTrue(q.hasWaitingConsumer());
+
+        assertTrue(q.offer(one));
+        assertEquals(0, q.getWaitingConsumerCount());
+        assertFalse(q.hasWaitingConsumer());
+
+        awaitTermination(t);
+    }
+
+    /**
+     * transfer(null) throws NullPointerException
+     */
+    public void testTransfer1() throws InterruptedException {
+        try {
+            LinkedTransferQueue q = new LinkedTransferQueue();
+            q.transfer(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * transfer waits until a poll occurs. The transfered element
+     * is returned by this associated poll.
+     */
+    public void testTransfer2() throws InterruptedException {
+        final LinkedTransferQueue<Integer> q
+            = new LinkedTransferQueue<Integer>();
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                threadStarted.countDown();
+                q.transfer(five);
+                checkEmpty(q);
+            }});
+
+        threadStarted.await();
+        waitForThreadToEnterWaitState(t);
+        assertEquals(1, q.size());
+        assertSame(five, q.poll());
+        checkEmpty(q);
+        awaitTermination(t);
+    }
+
+    /**
+     * transfer waits until a poll occurs, and then transfers in fifo order
+     */
+    public void testTransfer3() throws InterruptedException {
+        final LinkedTransferQueue<Integer> q
+            = new LinkedTransferQueue<Integer>();
+
+        Thread first = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.transfer(four);
+                assertTrue(!q.contains(four));
+                assertEquals(1, q.size());
+            }});
+
+        Thread interruptedThread = newStartedThread(
+            new CheckedInterruptedRunnable() {
+                public void realRun() throws InterruptedException {
+                    while (q.isEmpty())
+                        Thread.yield();
+                    q.transfer(five);
+                }});
+
+        while (q.size() < 2)
+            Thread.yield();
+        assertEquals(2, q.size());
+        assertSame(four, q.poll());
+        first.join();
+        assertEquals(1, q.size());
+        interruptedThread.interrupt();
+        interruptedThread.join();
+        checkEmpty(q);
+    }
+
+    /**
+     * transfer waits until a poll occurs, at which point the polling
+     * thread returns the element
+     */
+    public void testTransfer4() throws InterruptedException {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.transfer(four);
+                assertFalse(q.contains(four));
+                assertSame(three, q.poll());
+            }});
+
+        while (q.isEmpty())
+            Thread.yield();
+        assertFalse(q.isEmpty());
+        assertEquals(1, q.size());
+        assertTrue(q.offer(three));
+        assertSame(four, q.poll());
+        awaitTermination(t);
+    }
+
+    /**
+     * transfer waits until a take occurs. The transfered element
+     * is returned by this associated take.
+     */
+    public void testTransfer5() throws InterruptedException {
+        final LinkedTransferQueue<Integer> q
+            = new LinkedTransferQueue<Integer>();
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.transfer(four);
+                checkEmpty(q);
+            }});
+
+        while (q.isEmpty())
+            Thread.yield();
+        assertFalse(q.isEmpty());
+        assertEquals(1, q.size());
+        assertSame(four, q.take());
+        checkEmpty(q);
+        awaitTermination(t);
+    }
+
+    /**
+     * tryTransfer(null) throws NullPointerException
+     */
+    public void testTryTransfer1() {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+        try {
+            q.tryTransfer(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * tryTransfer returns false and does not enqueue if there are no
+     * consumers waiting to poll or take.
+     */
+    public void testTryTransfer2() throws InterruptedException {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+        assertFalse(q.tryTransfer(new Object()));
+        assertFalse(q.hasWaitingConsumer());
+        checkEmpty(q);
+    }
+
+    /**
+     * If there is a consumer waiting in timed poll, tryTransfer
+     * returns true while successfully transfering object.
+     */
+    public void testTryTransfer3() throws InterruptedException {
+        final Object hotPotato = new Object();
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                while (! q.hasWaitingConsumer())
+                    Thread.yield();
+                assertTrue(q.hasWaitingConsumer());
+                checkEmpty(q);
+                assertTrue(q.tryTransfer(hotPotato));
+            }});
+
+        long startTime = System.nanoTime();
+        assertSame(hotPotato, q.poll(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        checkEmpty(q);
+        awaitTermination(t);
+    }
+
+    /**
+     * If there is a consumer waiting in take, tryTransfer returns
+     * true while successfully transfering object.
+     */
+    public void testTryTransfer4() throws InterruptedException {
+        final Object hotPotato = new Object();
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                while (! q.hasWaitingConsumer())
+                    Thread.yield();
+                assertTrue(q.hasWaitingConsumer());
+                checkEmpty(q);
+                assertTrue(q.tryTransfer(hotPotato));
+            }});
+
+        assertSame(q.take(), hotPotato);
+        checkEmpty(q);
+        awaitTermination(t);
+    }
+
+    /**
+     * tryTransfer blocks interruptibly if no takers
+     */
+    public void testTryTransfer5() throws InterruptedException {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        assertTrue(q.isEmpty());
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                Thread.currentThread().interrupt();
+                try {
+                    q.tryTransfer(new Object(), LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.tryTransfer(new Object(), LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+                assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        checkEmpty(q);
+    }
+
+    /**
+     * tryTransfer gives up after the timeout and returns false
+     */
+    public void testTryTransfer6() throws InterruptedException {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                assertFalse(q.tryTransfer(new Object(),
+                                          timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                checkEmpty(q);
+            }});
+
+        awaitTermination(t);
+        checkEmpty(q);
+    }
+
+    /**
+     * tryTransfer waits for any elements previously in to be removed
+     * before transfering to a poll or take
+     */
+    public void testTryTransfer7() throws InterruptedException {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+        assertTrue(q.offer(four));
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                assertTrue(q.tryTransfer(five, LONG_DELAY_MS, MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+                checkEmpty(q);
+            }});
+
+        while (q.size() != 2)
+            Thread.yield();
+        assertEquals(2, q.size());
+        assertSame(four, q.poll());
+        assertSame(five, q.poll());
+        checkEmpty(q);
+        awaitTermination(t);
+    }
+
+    /**
+     * tryTransfer attempts to enqueue into the queue and fails
+     * returning false not enqueueing and the successive poll is null
+     */
+    public void testTryTransfer8() throws InterruptedException {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+        assertTrue(q.offer(four));
+        assertEquals(1, q.size());
+        long startTime = System.nanoTime();
+        assertFalse(q.tryTransfer(five, timeoutMillis(), MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        assertEquals(1, q.size());
+        assertSame(four, q.poll());
+        assertNull(q.poll());
+        checkEmpty(q);
+    }
+
+    private LinkedTransferQueue<Integer> populatedQueue(int n) {
+        LinkedTransferQueue<Integer> q = new LinkedTransferQueue<Integer>();
+        checkEmpty(q);
+        for (int i = 0; i < n; i++) {
+            assertEquals(i, q.size());
+            assertTrue(q.offer(i));
+            assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+        }
+        assertFalse(q.isEmpty());
+        return q;
+    }
+
+    /**
+     * remove(null), contains(null) always return false
+     */
+    public void testNeverContainsNull() {
+        Collection<?>[] qs = {
+            new LinkedTransferQueue<Object>(),
+            populatedQueue(2),
+        };
+
+        for (Collection<?> q : qs) {
+            assertFalse(q.contains(null));
+            assertFalse(q.remove(null));
+        }
+    }
+}
diff --git a/jdk/test/java/util/concurrent/tck/LockSupportTest.java b/jdk/test/java/util/concurrent/tck/LockSupportTest.java
new file mode 100644
index 00000000000..02175ad1051
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/LockSupportTest.java
@@ -0,0 +1,403 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea and Martin Buchholz with assistance from
+ * members of JCP JSR-166 Expert Group and released to the public
+ * domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.LockSupport;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class LockSupportTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(LockSupportTest.class);
+    }
+
+    static {
+        // Reduce the risk of rare disastrous classloading in first call to
+        // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773
+        Class<?> ensureLoaded = LockSupport.class;
+    }
+
+    /**
+     * Returns the blocker object used by tests in this file.
+     * Any old object will do; we'll return a convenient one.
+     */
+    static Object theBlocker() {
+        return LockSupportTest.class;
+    }
+
+    enum ParkMethod {
+        park() {
+            void park() {
+                LockSupport.park();
+            }
+            void park(long millis) {
+                throw new UnsupportedOperationException();
+            }
+        },
+        parkUntil() {
+            void park(long millis) {
+                LockSupport.parkUntil(deadline(millis));
+            }
+        },
+        parkNanos() {
+            void park(long millis) {
+                LockSupport.parkNanos(MILLISECONDS.toNanos(millis));
+            }
+        },
+        parkBlocker() {
+            void park() {
+                LockSupport.park(theBlocker());
+            }
+            void park(long millis) {
+                throw new UnsupportedOperationException();
+            }
+        },
+        parkUntilBlocker() {
+            void park(long millis) {
+                LockSupport.parkUntil(theBlocker(), deadline(millis));
+            }
+        },
+        parkNanosBlocker() {
+            void park(long millis) {
+                LockSupport.parkNanos(theBlocker(),
+                                      MILLISECONDS.toNanos(millis));
+            }
+        };
+
+        void park() { park(2 * LONG_DELAY_MS); }
+        abstract void park(long millis);
+
+        /** Returns a deadline to use with parkUntil. */
+        long deadline(long millis) {
+            // beware of rounding
+            return System.currentTimeMillis() + millis + 1;
+        }
+    }
+
+    /**
+     * park is released by subsequent unpark
+     */
+    public void testParkBeforeUnpark_park() {
+        testParkBeforeUnpark(ParkMethod.park);
+    }
+    public void testParkBeforeUnpark_parkNanos() {
+        testParkBeforeUnpark(ParkMethod.parkNanos);
+    }
+    public void testParkBeforeUnpark_parkUntil() {
+        testParkBeforeUnpark(ParkMethod.parkUntil);
+    }
+    public void testParkBeforeUnpark_parkBlocker() {
+        testParkBeforeUnpark(ParkMethod.parkBlocker);
+    }
+    public void testParkBeforeUnpark_parkNanosBlocker() {
+        testParkBeforeUnpark(ParkMethod.parkNanosBlocker);
+    }
+    public void testParkBeforeUnpark_parkUntilBlocker() {
+        testParkBeforeUnpark(ParkMethod.parkUntilBlocker);
+    }
+    public void testParkBeforeUnpark(final ParkMethod parkMethod) {
+        final CountDownLatch pleaseUnpark = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                pleaseUnpark.countDown();
+                parkMethod.park();
+            }});
+
+        await(pleaseUnpark);
+        LockSupport.unpark(t);
+        awaitTermination(t);
+    }
+
+    /**
+     * park is released by preceding unpark
+     */
+    public void testParkAfterUnpark_park() {
+        testParkAfterUnpark(ParkMethod.park);
+    }
+    public void testParkAfterUnpark_parkNanos() {
+        testParkAfterUnpark(ParkMethod.parkNanos);
+    }
+    public void testParkAfterUnpark_parkUntil() {
+        testParkAfterUnpark(ParkMethod.parkUntil);
+    }
+    public void testParkAfterUnpark_parkBlocker() {
+        testParkAfterUnpark(ParkMethod.parkBlocker);
+    }
+    public void testParkAfterUnpark_parkNanosBlocker() {
+        testParkAfterUnpark(ParkMethod.parkNanosBlocker);
+    }
+    public void testParkAfterUnpark_parkUntilBlocker() {
+        testParkAfterUnpark(ParkMethod.parkUntilBlocker);
+    }
+    public void testParkAfterUnpark(final ParkMethod parkMethod) {
+        final CountDownLatch pleaseUnpark = new CountDownLatch(1);
+        final AtomicBoolean pleasePark = new AtomicBoolean(false);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                pleaseUnpark.countDown();
+                while (!pleasePark.get())
+                    Thread.yield();
+                parkMethod.park();
+            }});
+
+        await(pleaseUnpark);
+        LockSupport.unpark(t);
+        pleasePark.set(true);
+        awaitTermination(t);
+    }
+
+    /**
+     * park is released by subsequent interrupt
+     */
+    public void testParkBeforeInterrupt_park() {
+        testParkBeforeInterrupt(ParkMethod.park);
+    }
+    public void testParkBeforeInterrupt_parkNanos() {
+        testParkBeforeInterrupt(ParkMethod.parkNanos);
+    }
+    public void testParkBeforeInterrupt_parkUntil() {
+        testParkBeforeInterrupt(ParkMethod.parkUntil);
+    }
+    public void testParkBeforeInterrupt_parkBlocker() {
+        testParkBeforeInterrupt(ParkMethod.parkBlocker);
+    }
+    public void testParkBeforeInterrupt_parkNanosBlocker() {
+        testParkBeforeInterrupt(ParkMethod.parkNanosBlocker);
+    }
+    public void testParkBeforeInterrupt_parkUntilBlocker() {
+        testParkBeforeInterrupt(ParkMethod.parkUntilBlocker);
+    }
+    public void testParkBeforeInterrupt(final ParkMethod parkMethod) {
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                pleaseInterrupt.countDown();
+                do {
+                    parkMethod.park();
+                    // park may return spuriously
+                } while (! Thread.currentThread().isInterrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * park is released by preceding interrupt
+     */
+    public void testParkAfterInterrupt_park() {
+        testParkAfterInterrupt(ParkMethod.park);
+    }
+    public void testParkAfterInterrupt_parkNanos() {
+        testParkAfterInterrupt(ParkMethod.parkNanos);
+    }
+    public void testParkAfterInterrupt_parkUntil() {
+        testParkAfterInterrupt(ParkMethod.parkUntil);
+    }
+    public void testParkAfterInterrupt_parkBlocker() {
+        testParkAfterInterrupt(ParkMethod.parkBlocker);
+    }
+    public void testParkAfterInterrupt_parkNanosBlocker() {
+        testParkAfterInterrupt(ParkMethod.parkNanosBlocker);
+    }
+    public void testParkAfterInterrupt_parkUntilBlocker() {
+        testParkAfterInterrupt(ParkMethod.parkUntilBlocker);
+    }
+    public void testParkAfterInterrupt(final ParkMethod parkMethod) {
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        final AtomicBoolean pleasePark = new AtomicBoolean(false);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                pleaseInterrupt.countDown();
+                while (!pleasePark.get())
+                    Thread.yield();
+                assertTrue(Thread.currentThread().isInterrupted());
+                parkMethod.park();
+                assertTrue(Thread.currentThread().isInterrupted());
+            }});
+
+        await(pleaseInterrupt);
+        t.interrupt();
+        pleasePark.set(true);
+        awaitTermination(t);
+    }
+
+    /**
+     * timed park times out if not unparked
+     */
+    public void testParkTimesOut_parkNanos() {
+        testParkTimesOut(ParkMethod.parkNanos);
+    }
+    public void testParkTimesOut_parkUntil() {
+        testParkTimesOut(ParkMethod.parkUntil);
+    }
+    public void testParkTimesOut_parkNanosBlocker() {
+        testParkTimesOut(ParkMethod.parkNanosBlocker);
+    }
+    public void testParkTimesOut_parkUntilBlocker() {
+        testParkTimesOut(ParkMethod.parkUntilBlocker);
+    }
+    public void testParkTimesOut(final ParkMethod parkMethod) {
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                for (;;) {
+                    long startTime = System.nanoTime();
+                    parkMethod.park(timeoutMillis());
+                    // park may return spuriously
+                    if (millisElapsedSince(startTime) >= timeoutMillis())
+                        return;
+                }
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * getBlocker(null) throws NullPointerException
+     */
+    public void testGetBlockerNull() {
+        try {
+            LockSupport.getBlocker(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * getBlocker returns the blocker object passed to park
+     */
+    public void testGetBlocker_parkBlocker() {
+        testGetBlocker(ParkMethod.parkBlocker);
+    }
+    public void testGetBlocker_parkNanosBlocker() {
+        testGetBlocker(ParkMethod.parkNanosBlocker);
+    }
+    public void testGetBlocker_parkUntilBlocker() {
+        testGetBlocker(ParkMethod.parkUntilBlocker);
+    }
+    public void testGetBlocker(final ParkMethod parkMethod) {
+        final CountDownLatch started = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                Thread t = Thread.currentThread();
+                started.countDown();
+                do {
+                    assertNull(LockSupport.getBlocker(t));
+                    parkMethod.park();
+                    assertNull(LockSupport.getBlocker(t));
+                    // park may return spuriously
+                } while (! Thread.currentThread().isInterrupted());
+            }});
+
+        long startTime = System.nanoTime();
+        await(started);
+        for (;;) {
+            Object x = LockSupport.getBlocker(t);
+            if (x == theBlocker()) { // success
+                t.interrupt();
+                awaitTermination(t);
+                assertNull(LockSupport.getBlocker(t));
+                return;
+            } else {
+                assertNull(x);  // ok
+                if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                    fail("timed out");
+                Thread.yield();
+            }
+        }
+    }
+
+    /**
+     * timed park(0) returns immediately.
+     *
+     * Requires hotspot fix for:
+     * 6763959 java.util.concurrent.locks.LockSupport.parkUntil(0) blocks forever
+     * which is in jdk7-b118 and 6u25.
+     */
+    public void testPark0_parkNanos() {
+        testPark0(ParkMethod.parkNanos);
+    }
+    public void testPark0_parkUntil() {
+        testPark0(ParkMethod.parkUntil);
+    }
+    public void testPark0_parkNanosBlocker() {
+        testPark0(ParkMethod.parkNanosBlocker);
+    }
+    public void testPark0_parkUntilBlocker() {
+        testPark0(ParkMethod.parkUntilBlocker);
+    }
+    public void testPark0(final ParkMethod parkMethod) {
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                parkMethod.park(0L);
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * timed park(Long.MIN_VALUE) returns immediately.
+     */
+    public void testParkNeg_parkNanos() {
+        testParkNeg(ParkMethod.parkNanos);
+    }
+    public void testParkNeg_parkUntil() {
+        testParkNeg(ParkMethod.parkUntil);
+    }
+    public void testParkNeg_parkNanosBlocker() {
+        testParkNeg(ParkMethod.parkNanosBlocker);
+    }
+    public void testParkNeg_parkUntilBlocker() {
+        testParkNeg(ParkMethod.parkUntilBlocker);
+    }
+    public void testParkNeg(final ParkMethod parkMethod) {
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                parkMethod.park(Long.MIN_VALUE);
+            }});
+
+        awaitTermination(t);
+    }
+}
diff --git a/jdk/test/java/util/concurrent/tck/LongAccumulatorTest.java b/jdk/test/java/util/concurrent/tck/LongAccumulatorTest.java
new file mode 100644
index 00000000000..6acfd27efc7
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/LongAccumulatorTest.java
@@ -0,0 +1,183 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Phaser;
+import java.util.concurrent.atomic.LongAccumulator;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class LongAccumulatorTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(LongAccumulatorTest.class);
+    }
+
+    /**
+     * default constructed initializes to zero
+     */
+    public void testConstructor() {
+        LongAccumulator ai = new LongAccumulator(Long::max, 0L);
+        assertEquals(0, ai.get());
+    }
+
+    /**
+     * accumulate accumulates given value to current, and get returns current value
+     */
+    public void testAccumulateAndGet() {
+        LongAccumulator ai = new LongAccumulator(Long::max, 0L);
+        ai.accumulate(2);
+        assertEquals(2, ai.get());
+        ai.accumulate(-4);
+        assertEquals(2, ai.get());
+        ai.accumulate(4);
+        assertEquals(4, ai.get());
+    }
+
+    /**
+     * reset() causes subsequent get() to return zero
+     */
+    public void testReset() {
+        LongAccumulator ai = new LongAccumulator(Long::max, 0L);
+        ai.accumulate(2);
+        assertEquals(2, ai.get());
+        ai.reset();
+        assertEquals(0, ai.get());
+    }
+
+    /**
+     * getThenReset() returns current value; subsequent get() returns zero
+     */
+    public void testGetThenReset() {
+        LongAccumulator ai = new LongAccumulator(Long::max, 0L);
+        ai.accumulate(2);
+        assertEquals(2, ai.get());
+        assertEquals(2, ai.getThenReset());
+        assertEquals(0, ai.get());
+    }
+
+    /**
+     * toString returns current value.
+     */
+    public void testToString() {
+        LongAccumulator ai = new LongAccumulator(Long::max, 0L);
+        assertEquals("0", ai.toString());
+        ai.accumulate(1);
+        assertEquals(Long.toString(1), ai.toString());
+    }
+
+    /**
+     * intValue returns current value.
+     */
+    public void testIntValue() {
+        LongAccumulator ai = new LongAccumulator(Long::max, 0L);
+        assertEquals(0, ai.intValue());
+        ai.accumulate(1);
+        assertEquals(1, ai.intValue());
+    }
+
+    /**
+     * longValue returns current value.
+     */
+    public void testLongValue() {
+        LongAccumulator ai = new LongAccumulator(Long::max, 0L);
+        assertEquals(0, ai.longValue());
+        ai.accumulate(1);
+        assertEquals(1, ai.longValue());
+    }
+
+    /**
+     * floatValue returns current value.
+     */
+    public void testFloatValue() {
+        LongAccumulator ai = new LongAccumulator(Long::max, 0L);
+        assertEquals(0.0f, ai.floatValue());
+        ai.accumulate(1);
+        assertEquals(1.0f, ai.floatValue());
+    }
+
+    /**
+     * doubleValue returns current value.
+     */
+    public void testDoubleValue() {
+        LongAccumulator ai = new LongAccumulator(Long::max, 0L);
+        assertEquals(0.0, ai.doubleValue());
+        ai.accumulate(1);
+        assertEquals(1.0, ai.doubleValue());
+    }
+
+    /**
+     * accumulates by multiple threads produce correct result
+     */
+    public void testAccumulateAndGetMT() {
+        final int incs = 1000000;
+        final int nthreads = 4;
+        final ExecutorService pool = Executors.newCachedThreadPool();
+        LongAccumulator a = new LongAccumulator(Long::max, 0L);
+        Phaser phaser = new Phaser(nthreads + 1);
+        for (int i = 0; i < nthreads; ++i)
+            pool.execute(new AccTask(a, phaser, incs));
+        phaser.arriveAndAwaitAdvance();
+        phaser.arriveAndAwaitAdvance();
+        long expected = incs - 1;
+        long result = a.get();
+        assertEquals(expected, result);
+        pool.shutdown();
+    }
+
+    static final class AccTask implements Runnable {
+        final LongAccumulator acc;
+        final Phaser phaser;
+        final int incs;
+        volatile long result;
+        AccTask(LongAccumulator acc, Phaser phaser, int incs) {
+            this.acc = acc;
+            this.phaser = phaser;
+            this.incs = incs;
+        }
+
+        public void run() {
+            phaser.arriveAndAwaitAdvance();
+            LongAccumulator a = acc;
+            for (int i = 0; i < incs; ++i)
+                a.accumulate(i);
+            result = a.get();
+            phaser.arrive();
+        }
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/LongAdderTest.java b/jdk/test/java/util/concurrent/tck/LongAdderTest.java
new file mode 100644
index 00000000000..4f0d5e07f75
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/LongAdderTest.java
@@ -0,0 +1,220 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.atomic.LongAdder;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class LongAdderTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(LongAdderTest.class);
+    }
+
+    /**
+     * default constructed initializes to zero
+     */
+    public void testConstructor() {
+        LongAdder ai = new LongAdder();
+        assertEquals(0, ai.sum());
+    }
+
+    /**
+     * add adds given value to current, and sum returns current value
+     */
+    public void testAddAndSum() {
+        LongAdder ai = new LongAdder();
+        ai.add(2);
+        assertEquals(2, ai.sum());
+        ai.add(-4);
+        assertEquals(-2, ai.sum());
+    }
+
+    /**
+     * decrement decrements and sum returns current value
+     */
+    public void testDecrementAndsum() {
+        LongAdder ai = new LongAdder();
+        ai.decrement();
+        assertEquals(-1, ai.sum());
+        ai.decrement();
+        assertEquals(-2, ai.sum());
+    }
+
+    /**
+     * incrementAndGet increments and returns current value
+     */
+    public void testIncrementAndsum() {
+        LongAdder ai = new LongAdder();
+        ai.increment();
+        assertEquals(1, ai.sum());
+        ai.increment();
+        assertEquals(2, ai.sum());
+    }
+
+    /**
+     * reset() causes subsequent sum() to return zero
+     */
+    public void testReset() {
+        LongAdder ai = new LongAdder();
+        ai.add(2);
+        assertEquals(2, ai.sum());
+        ai.reset();
+        assertEquals(0, ai.sum());
+    }
+
+    /**
+     * sumThenReset() returns sum; subsequent sum() returns zero
+     */
+    public void testSumThenReset() {
+        LongAdder ai = new LongAdder();
+        ai.add(2);
+        assertEquals(2, ai.sum());
+        assertEquals(2, ai.sumThenReset());
+        assertEquals(0, ai.sum());
+    }
+
+    /**
+     * a deserialized serialized adder holds same value
+     */
+    public void testSerialization() throws Exception {
+        LongAdder x = new LongAdder();
+        LongAdder y = serialClone(x);
+        assertNotSame(x, y);
+        x.add(-22);
+        LongAdder z = serialClone(x);
+        assertNotSame(y, z);
+        assertEquals(-22, x.sum());
+        assertEquals(0, y.sum());
+        assertEquals(-22, z.sum());
+    }
+
+    /**
+     * toString returns current value.
+     */
+    public void testToString() {
+        LongAdder ai = new LongAdder();
+        assertEquals("0", ai.toString());
+        ai.increment();
+        assertEquals(Long.toString(1), ai.toString());
+    }
+
+    /**
+     * intValue returns current value.
+     */
+    public void testIntValue() {
+        LongAdder ai = new LongAdder();
+        assertEquals(0, ai.intValue());
+        ai.increment();
+        assertEquals(1, ai.intValue());
+    }
+
+    /**
+     * longValue returns current value.
+     */
+    public void testLongValue() {
+        LongAdder ai = new LongAdder();
+        assertEquals(0, ai.longValue());
+        ai.increment();
+        assertEquals(1, ai.longValue());
+    }
+
+    /**
+     * floatValue returns current value.
+     */
+    public void testFloatValue() {
+        LongAdder ai = new LongAdder();
+        assertEquals(0.0f, ai.floatValue());
+        ai.increment();
+        assertEquals(1.0f, ai.floatValue());
+    }
+
+    /**
+     * doubleValue returns current value.
+     */
+    public void testDoubleValue() {
+        LongAdder ai = new LongAdder();
+        assertEquals(0.0, ai.doubleValue());
+        ai.increment();
+        assertEquals(1.0, ai.doubleValue());
+    }
+
+    /**
+     * adds by multiple threads produce correct sum
+     */
+    public void testAddAndSumMT() throws Throwable {
+        final int incs = 1000000;
+        final int nthreads = 4;
+        final ExecutorService pool = Executors.newCachedThreadPool();
+        LongAdder a = new LongAdder();
+        CyclicBarrier barrier = new CyclicBarrier(nthreads + 1);
+        for (int i = 0; i < nthreads; ++i)
+            pool.execute(new AdderTask(a, barrier, incs));
+        barrier.await();
+        barrier.await();
+        long total = (long)nthreads * incs;
+        long sum = a.sum();
+        assertEquals(sum, total);
+        pool.shutdown();
+    }
+
+    static final class AdderTask implements Runnable {
+        final LongAdder adder;
+        final CyclicBarrier barrier;
+        final int incs;
+        volatile long result;
+        AdderTask(LongAdder adder, CyclicBarrier barrier, int incs) {
+            this.adder = adder;
+            this.barrier = barrier;
+            this.incs = incs;
+        }
+
+        public void run() {
+            try {
+                barrier.await();
+                LongAdder a = adder;
+                for (int i = 0; i < incs; ++i)
+                    a.add(1L);
+                result = a.sum();
+                barrier.await();
+            } catch (Throwable t) { throw new Error(t); }
+        }
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/PhaserTest.java b/jdk/test/java/util/concurrent/tck/PhaserTest.java
new file mode 100644
index 00000000000..69b6c808ff8
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/PhaserTest.java
@@ -0,0 +1,820 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include John Vint
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Phaser;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class PhaserTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(PhaserTest.class);
+    }
+
+    private static final int maxParties = 65535;
+
+    /** Checks state of unterminated phaser. */
+    protected void assertState(Phaser phaser,
+                               int phase, int parties, int unarrived) {
+        assertEquals(phase, phaser.getPhase());
+        assertEquals(parties, phaser.getRegisteredParties());
+        assertEquals(unarrived, phaser.getUnarrivedParties());
+        assertEquals(parties - unarrived, phaser.getArrivedParties());
+        assertFalse(phaser.isTerminated());
+    }
+
+    /** Checks state of terminated phaser. */
+    protected void assertTerminated(Phaser phaser, int maxPhase, int parties) {
+        assertTrue(phaser.isTerminated());
+        int expectedPhase = maxPhase + Integer.MIN_VALUE;
+        assertEquals(expectedPhase, phaser.getPhase());
+        assertEquals(parties, phaser.getRegisteredParties());
+        assertEquals(expectedPhase, phaser.register());
+        assertEquals(expectedPhase, phaser.arrive());
+        assertEquals(expectedPhase, phaser.arriveAndDeregister());
+    }
+
+    protected void assertTerminated(Phaser phaser, int maxPhase) {
+        assertTerminated(phaser, maxPhase, 0);
+    }
+
+    /**
+     * Empty constructor builds a new Phaser with no parent, no registered
+     * parties and initial phase number of 0
+     */
+    public void testConstructorDefaultValues() {
+        Phaser phaser = new Phaser();
+        assertNull(phaser.getParent());
+        assertEquals(0, phaser.getRegisteredParties());
+        assertEquals(0, phaser.getArrivedParties());
+        assertEquals(0, phaser.getUnarrivedParties());
+        assertEquals(0, phaser.getPhase());
+    }
+
+    /**
+     * Constructing with a negative number of parties throws
+     * IllegalArgumentException
+     */
+    public void testConstructorNegativeParties() {
+        try {
+            new Phaser(-1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructing with a negative number of parties throws
+     * IllegalArgumentException
+     */
+    public void testConstructorNegativeParties2() {
+        try {
+            new Phaser(new Phaser(), -1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructing with a number of parties > 65535 throws
+     * IllegalArgumentException
+     */
+    public void testConstructorPartiesExceedsLimit() {
+        new Phaser(maxParties);
+        try {
+            new Phaser(maxParties + 1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+
+        new Phaser(new Phaser(), maxParties);
+        try {
+            new Phaser(new Phaser(), maxParties + 1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * The parent provided to the constructor should be returned from
+     * a later call to getParent
+     */
+    public void testConstructor3() {
+        Phaser parent = new Phaser();
+        assertSame(parent, new Phaser(parent).getParent());
+        assertNull(new Phaser(null).getParent());
+    }
+
+    /**
+     * The parent being input into the parameter should equal the original
+     * parent when being returned
+     */
+    public void testConstructor5() {
+        Phaser parent = new Phaser();
+        assertSame(parent, new Phaser(parent, 0).getParent());
+        assertNull(new Phaser(null, 0).getParent());
+    }
+
+    /**
+     * register() will increment the number of unarrived parties by
+     * one and not affect its arrived parties
+     */
+    public void testRegister1() {
+        Phaser phaser = new Phaser();
+        assertState(phaser, 0, 0, 0);
+        assertEquals(0, phaser.register());
+        assertState(phaser, 0, 1, 1);
+    }
+
+    /**
+     * Registering more than 65536 parties causes IllegalStateException
+     */
+    public void testRegister2() {
+        Phaser phaser = new Phaser(0);
+        assertState(phaser, 0, 0, 0);
+        assertEquals(0, phaser.bulkRegister(maxParties - 10));
+        assertState(phaser, 0, maxParties - 10, maxParties - 10);
+        for (int i = 0; i < 10; i++) {
+            assertState(phaser, 0, maxParties - 10 + i, maxParties - 10 + i);
+            assertEquals(0, phaser.register());
+        }
+        assertState(phaser, 0, maxParties, maxParties);
+        try {
+            phaser.register();
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+
+        try {
+            phaser.bulkRegister(Integer.MAX_VALUE);
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+
+        assertEquals(0, phaser.bulkRegister(0));
+        assertState(phaser, 0, maxParties, maxParties);
+    }
+
+    /**
+     * register() correctly returns the current barrier phase number
+     * when invoked
+     */
+    public void testRegister3() {
+        Phaser phaser = new Phaser();
+        assertEquals(0, phaser.register());
+        assertEquals(0, phaser.arrive());
+        assertEquals(1, phaser.register());
+        assertState(phaser, 1, 2, 2);
+    }
+
+    /**
+     * register causes the next arrive to not increment the phase
+     * rather retain the phase number
+     */
+    public void testRegister4() {
+        Phaser phaser = new Phaser(1);
+        assertEquals(0, phaser.arrive());
+        assertEquals(1, phaser.register());
+        assertEquals(1, phaser.arrive());
+        assertState(phaser, 1, 2, 1);
+    }
+
+    /**
+     * register on a subphaser that is currently empty succeeds, even
+     * in the presence of another non-empty subphaser
+     */
+    public void testRegisterEmptySubPhaser() {
+        Phaser root = new Phaser();
+        Phaser child1 = new Phaser(root, 1);
+        Phaser child2 = new Phaser(root, 0);
+        assertEquals(0, child2.register());
+        assertState(root, 0, 2, 2);
+        assertState(child1, 0, 1, 1);
+        assertState(child2, 0, 1, 1);
+        assertEquals(0, child2.arriveAndDeregister());
+        assertState(root, 0, 1, 1);
+        assertState(child1, 0, 1, 1);
+        assertState(child2, 0, 0, 0);
+        assertEquals(0, child2.register());
+        assertEquals(0, child2.arriveAndDeregister());
+        assertState(root, 0, 1, 1);
+        assertState(child1, 0, 1, 1);
+        assertState(child2, 0, 0, 0);
+        assertEquals(0, child1.arriveAndDeregister());
+        assertTerminated(root, 1);
+        assertTerminated(child1, 1);
+        assertTerminated(child2, 1);
+    }
+
+    /**
+     * Invoking bulkRegister with a negative parameter throws an
+     * IllegalArgumentException
+     */
+    public void testBulkRegister1() {
+        try {
+            new Phaser().bulkRegister(-1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * bulkRegister should correctly record the number of unarrived
+     * parties with the number of parties being registered
+     */
+    public void testBulkRegister2() {
+        Phaser phaser = new Phaser();
+        assertEquals(0, phaser.bulkRegister(0));
+        assertState(phaser, 0, 0, 0);
+        assertEquals(0, phaser.bulkRegister(20));
+        assertState(phaser, 0, 20, 20);
+    }
+
+    /**
+     * Registering with a number of parties greater than or equal to 1<<16
+     * throws IllegalStateException.
+     */
+    public void testBulkRegister3() {
+        assertEquals(0, new Phaser().bulkRegister((1 << 16) - 1));
+
+        try {
+            new Phaser().bulkRegister(1 << 16);
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+
+        try {
+            new Phaser(2).bulkRegister((1 << 16) - 2);
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * the phase number increments correctly when tripping the barrier
+     */
+    public void testPhaseIncrement1() {
+        for (int size = 1; size < nine; size++) {
+            final Phaser phaser = new Phaser(size);
+            for (int index = 0; index <= (1 << size); index++) {
+                int phase = phaser.arrive();
+                assertTrue(index % size == 0 ? (index / size) == phase : index - (phase * size) > 0);
+            }
+        }
+    }
+
+    /**
+     * arrive() on a registered phaser increments phase.
+     */
+    public void testArrive1() {
+        Phaser phaser = new Phaser(1);
+        assertState(phaser, 0, 1, 1);
+        assertEquals(0, phaser.arrive());
+        assertState(phaser, 1, 1, 1);
+    }
+
+    /**
+     * arriveAndDeregister does not wait for others to arrive at barrier
+     */
+    public void testArriveAndDeregister() {
+        final Phaser phaser = new Phaser(1);
+        for (int i = 0; i < 10; i++) {
+            assertState(phaser, 0, 1, 1);
+            assertEquals(0, phaser.register());
+            assertState(phaser, 0, 2, 2);
+            assertEquals(0, phaser.arriveAndDeregister());
+            assertState(phaser, 0, 1, 1);
+        }
+        assertEquals(0, phaser.arriveAndDeregister());
+        assertTerminated(phaser, 1);
+    }
+
+    /**
+     * arriveAndDeregister does not wait for others to arrive at barrier
+     */
+    public void testArrive2() {
+        final Phaser phaser = new Phaser();
+        assertEquals(0, phaser.register());
+        List<Thread> threads = new ArrayList<Thread>();
+        for (int i = 0; i < 10; i++) {
+            assertEquals(0, phaser.register());
+            threads.add(newStartedThread(new CheckedRunnable() {
+                public void realRun() {
+                    assertEquals(0, phaser.arriveAndDeregister());
+                }}));
+        }
+
+        for (Thread thread : threads)
+            awaitTermination(thread);
+        assertState(phaser, 0, 1, 1);
+        assertEquals(0, phaser.arrive());
+        assertState(phaser, 1, 1, 1);
+    }
+
+    /**
+     * arrive() returns a negative number if the Phaser is terminated
+     */
+    public void testArrive3() {
+        Phaser phaser = new Phaser(1);
+        phaser.forceTermination();
+        assertTerminated(phaser, 0, 1);
+        assertEquals(0, phaser.getPhase() + Integer.MIN_VALUE);
+        assertTrue(phaser.arrive() < 0);
+        assertTrue(phaser.register() < 0);
+        assertTrue(phaser.arriveAndDeregister() < 0);
+        assertTrue(phaser.awaitAdvance(1) < 0);
+        assertTrue(phaser.getPhase() < 0);
+    }
+
+    /**
+     * arriveAndDeregister() throws IllegalStateException if number of
+     * registered or unarrived parties would become negative
+     */
+    public void testArriveAndDeregister1() {
+        Phaser phaser = new Phaser();
+        try {
+            phaser.arriveAndDeregister();
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * arriveAndDeregister reduces the number of arrived parties
+     */
+    public void testArriveAndDeregister2() {
+        final Phaser phaser = new Phaser(1);
+        assertEquals(0, phaser.register());
+        assertEquals(0, phaser.arrive());
+        assertState(phaser, 0, 2, 1);
+        assertEquals(0, phaser.arriveAndDeregister());
+        assertState(phaser, 1, 1, 1);
+    }
+
+    /**
+     * arriveAndDeregister arrives at the barrier on a phaser with a parent and
+     * when a deregistration occurs and causes the phaser to have zero parties
+     * its parent will be deregistered as well
+     */
+    public void testArriveAndDeregister3() {
+        Phaser parent = new Phaser();
+        Phaser child = new Phaser(parent);
+        assertState(child, 0, 0, 0);
+        assertState(parent, 0, 0, 0);
+        assertEquals(0, child.register());
+        assertState(child, 0, 1, 1);
+        assertState(parent, 0, 1, 1);
+        assertEquals(0, child.arriveAndDeregister());
+        assertTerminated(child, 1);
+        assertTerminated(parent, 1);
+    }
+
+    /**
+     * arriveAndDeregister deregisters one party from its parent when
+     * the number of parties of child is zero after deregistration
+     */
+    public void testArriveAndDeregister4() {
+        Phaser parent = new Phaser();
+        Phaser child = new Phaser(parent);
+        assertEquals(0, parent.register());
+        assertEquals(0, child.register());
+        assertState(child, 0, 1, 1);
+        assertState(parent, 0, 2, 2);
+        assertEquals(0, child.arriveAndDeregister());
+        assertState(child, 0, 0, 0);
+        assertState(parent, 0, 1, 1);
+    }
+
+    /**
+     * arriveAndDeregister deregisters one party from its parent when
+     * the number of parties of root is nonzero after deregistration.
+     */
+    public void testArriveAndDeregister5() {
+        Phaser root = new Phaser();
+        Phaser parent = new Phaser(root);
+        Phaser child = new Phaser(parent);
+        assertState(root, 0, 0, 0);
+        assertState(parent, 0, 0, 0);
+        assertState(child, 0, 0, 0);
+        assertEquals(0, child.register());
+        assertState(root, 0, 1, 1);
+        assertState(parent, 0, 1, 1);
+        assertState(child, 0, 1, 1);
+        assertEquals(0, child.arriveAndDeregister());
+        assertTerminated(child, 1);
+        assertTerminated(parent, 1);
+        assertTerminated(root, 1);
+    }
+
+    /**
+     * arriveAndDeregister returns the phase in which it leaves the
+     * phaser in after deregistration
+     */
+    public void testArriveAndDeregister6() {
+        final Phaser phaser = new Phaser(2);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertEquals(0, phaser.arrive());
+            }});
+        assertEquals(1, phaser.arriveAndAwaitAdvance());
+        assertState(phaser, 1, 2, 2);
+        assertEquals(1, phaser.arriveAndDeregister());
+        assertState(phaser, 1, 1, 1);
+        assertEquals(1, phaser.arriveAndDeregister());
+        assertTerminated(phaser, 2);
+        awaitTermination(t);
+    }
+
+    /**
+     * awaitAdvance succeeds upon advance
+     */
+    public void testAwaitAdvance1() {
+        final Phaser phaser = new Phaser(1);
+        assertEquals(0, phaser.arrive());
+        assertEquals(1, phaser.awaitAdvance(0));
+    }
+
+    /**
+     * awaitAdvance with a negative parameter will return without affecting the
+     * phaser
+     */
+    public void testAwaitAdvance2() {
+        Phaser phaser = new Phaser();
+        assertTrue(phaser.awaitAdvance(-1) < 0);
+        assertState(phaser, 0, 0, 0);
+    }
+
+    /**
+     * awaitAdvanceInterruptibly blocks interruptibly
+     */
+    public void testAwaitAdvanceInterruptibly_interruptible() throws InterruptedException {
+        final Phaser phaser = new Phaser(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(2);
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                Thread.currentThread().interrupt();
+                try {
+                    phaser.awaitAdvanceInterruptibly(0);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    phaser.awaitAdvanceInterruptibly(0);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws TimeoutException {
+                Thread.currentThread().interrupt();
+                try {
+                    phaser.awaitAdvanceInterruptibly(0, 2*LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    phaser.awaitAdvanceInterruptibly(0, 2*LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertState(phaser, 0, 1, 1);
+        assertThreadsStayAlive(t1, t2);
+        t1.interrupt();
+        t2.interrupt();
+        awaitTermination(t1);
+        awaitTermination(t2);
+        assertState(phaser, 0, 1, 1);
+        assertEquals(0, phaser.arrive());
+        assertState(phaser, 1, 1, 1);
+    }
+
+    /**
+     * awaitAdvance continues waiting if interrupted before waiting
+     */
+    public void testAwaitAdvanceAfterInterrupt() {
+        final Phaser phaser = new Phaser();
+        assertEquals(0, phaser.register());
+        final CountDownLatch pleaseArrive = new CountDownLatch(1);
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                Thread.currentThread().interrupt();
+                assertEquals(0, phaser.register());
+                assertEquals(0, phaser.arrive());
+                pleaseArrive.countDown();
+                assertTrue(Thread.currentThread().isInterrupted());
+                assertEquals(1, phaser.awaitAdvance(0));
+                assertTrue(Thread.interrupted());
+            }});
+
+        await(pleaseArrive);
+        waitForThreadToEnterWaitState(t, SHORT_DELAY_MS);
+        assertEquals(0, phaser.arrive());
+        awaitTermination(t);
+
+        Thread.currentThread().interrupt();
+        assertEquals(1, phaser.awaitAdvance(0));
+        assertTrue(Thread.interrupted());
+    }
+
+    /**
+     *  awaitAdvance continues waiting if interrupted while waiting
+     */
+    public void testAwaitAdvanceBeforeInterrupt() {
+        final Phaser phaser = new Phaser();
+        assertEquals(0, phaser.register());
+        final CountDownLatch pleaseArrive = new CountDownLatch(1);
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertEquals(0, phaser.register());
+                assertEquals(0, phaser.arrive());
+                assertFalse(Thread.currentThread().isInterrupted());
+                pleaseArrive.countDown();
+                assertEquals(1, phaser.awaitAdvance(0));
+                assertTrue(Thread.interrupted());
+            }});
+
+        await(pleaseArrive);
+        waitForThreadToEnterWaitState(t, SHORT_DELAY_MS);
+        t.interrupt();
+        assertEquals(0, phaser.arrive());
+        awaitTermination(t);
+
+        Thread.currentThread().interrupt();
+        assertEquals(1, phaser.awaitAdvance(0));
+        assertTrue(Thread.interrupted());
+    }
+
+    /**
+     * arriveAndAwaitAdvance continues waiting if interrupted before waiting
+     */
+    public void testArriveAndAwaitAdvanceAfterInterrupt() {
+        final Phaser phaser = new Phaser();
+        assertEquals(0, phaser.register());
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                Thread.currentThread().interrupt();
+                assertEquals(0, phaser.register());
+                pleaseInterrupt.countDown();
+                assertTrue(Thread.currentThread().isInterrupted());
+                assertEquals(1, phaser.arriveAndAwaitAdvance());
+                assertTrue(Thread.currentThread().isInterrupted());
+            }});
+
+        await(pleaseInterrupt);
+        waitForThreadToEnterWaitState(t, SHORT_DELAY_MS);
+        Thread.currentThread().interrupt();
+        assertEquals(1, phaser.arriveAndAwaitAdvance());
+        assertTrue(Thread.interrupted());
+        awaitTermination(t);
+    }
+
+    /**
+     * arriveAndAwaitAdvance continues waiting if interrupted while waiting
+     */
+    public void testArriveAndAwaitAdvanceBeforeInterrupt() {
+        final Phaser phaser = new Phaser();
+        assertEquals(0, phaser.register());
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertEquals(0, phaser.register());
+                assertFalse(Thread.currentThread().isInterrupted());
+                pleaseInterrupt.countDown();
+                assertEquals(1, phaser.arriveAndAwaitAdvance());
+                assertTrue(Thread.currentThread().isInterrupted());
+            }});
+
+        await(pleaseInterrupt);
+        waitForThreadToEnterWaitState(t, SHORT_DELAY_MS);
+        t.interrupt();
+        Thread.currentThread().interrupt();
+        assertEquals(1, phaser.arriveAndAwaitAdvance());
+        assertTrue(Thread.interrupted());
+        awaitTermination(t);
+    }
+
+    /**
+     * awaitAdvance atomically waits for all parties within the same phase to
+     * complete before continuing
+     */
+    public void testAwaitAdvance4() {
+        final Phaser phaser = new Phaser(4);
+        final AtomicInteger count = new AtomicInteger(0);
+        List<Thread> threads = new ArrayList<Thread>();
+        for (int i = 0; i < 4; i++)
+            threads.add(newStartedThread(new CheckedRunnable() {
+                public void realRun() {
+                    for (int k = 0; k < 3; k++) {
+                        assertEquals(2 * k + 1, phaser.arriveAndAwaitAdvance());
+                        count.incrementAndGet();
+                        assertEquals(2 * k + 1, phaser.arrive());
+                        assertEquals(2 * k + 2, phaser.awaitAdvance(2 * k + 1));
+                        assertEquals(4 * (k + 1), count.get());
+                    }}}));
+
+        for (Thread thread : threads)
+            awaitTermination(thread);
+    }
+
+    /**
+     * awaitAdvance returns the current phase
+     */
+    public void testAwaitAdvance5() {
+        final Phaser phaser = new Phaser(1);
+        assertEquals(1, phaser.awaitAdvance(phaser.arrive()));
+        assertEquals(1, phaser.getPhase());
+        assertEquals(1, phaser.register());
+        List<Thread> threads = new ArrayList<Thread>();
+        for (int i = 0; i < 8; i++) {
+            final CountDownLatch latch = new CountDownLatch(1);
+            final boolean goesFirst = ((i & 1) == 0);
+            threads.add(newStartedThread(new CheckedRunnable() {
+                public void realRun() {
+                    if (goesFirst)
+                        latch.countDown();
+                    else
+                        await(latch);
+                    phaser.arrive();
+                }}));
+            if (goesFirst)
+                await(latch);
+            else
+                latch.countDown();
+            assertEquals(i + 2, phaser.awaitAdvance(phaser.arrive()));
+            assertEquals(i + 2, phaser.getPhase());
+        }
+        for (Thread thread : threads)
+            awaitTermination(thread);
+    }
+
+    /**
+     * awaitAdvance returns the current phase in child phasers
+     */
+    public void testAwaitAdvanceTieredPhaser() throws Exception {
+        final Phaser parent = new Phaser();
+        final List<Phaser> zeroPartyChildren = new ArrayList<Phaser>(3);
+        final List<Phaser> onePartyChildren = new ArrayList<Phaser>(3);
+        for (int i = 0; i < 3; i++) {
+            zeroPartyChildren.add(new Phaser(parent, 0));
+            onePartyChildren.add(new Phaser(parent, 1));
+        }
+        final List<Phaser> phasers = new ArrayList<Phaser>();
+        phasers.addAll(zeroPartyChildren);
+        phasers.addAll(onePartyChildren);
+        phasers.add(parent);
+        for (Phaser phaser : phasers) {
+            assertEquals(-42, phaser.awaitAdvance(-42));
+            assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42));
+            assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42, MEDIUM_DELAY_MS, MILLISECONDS));
+        }
+
+        for (Phaser child : onePartyChildren)
+            assertEquals(0, child.arrive());
+        for (Phaser phaser : phasers) {
+            assertEquals(-42, phaser.awaitAdvance(-42));
+            assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42));
+            assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42, MEDIUM_DELAY_MS, MILLISECONDS));
+            assertEquals(1, phaser.awaitAdvance(0));
+            assertEquals(1, phaser.awaitAdvanceInterruptibly(0));
+            assertEquals(1, phaser.awaitAdvanceInterruptibly(0, MEDIUM_DELAY_MS, MILLISECONDS));
+        }
+
+        for (Phaser child : onePartyChildren)
+            assertEquals(1, child.arrive());
+        for (Phaser phaser : phasers) {
+            assertEquals(-42, phaser.awaitAdvance(-42));
+            assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42));
+            assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42, MEDIUM_DELAY_MS, MILLISECONDS));
+            assertEquals(2, phaser.awaitAdvance(0));
+            assertEquals(2, phaser.awaitAdvanceInterruptibly(0));
+            assertEquals(2, phaser.awaitAdvanceInterruptibly(0, MEDIUM_DELAY_MS, MILLISECONDS));
+            assertEquals(2, phaser.awaitAdvance(1));
+            assertEquals(2, phaser.awaitAdvanceInterruptibly(1));
+            assertEquals(2, phaser.awaitAdvanceInterruptibly(1, MEDIUM_DELAY_MS, MILLISECONDS));
+        }
+    }
+
+    /**
+     * awaitAdvance returns when the phaser is externally terminated
+     */
+    public void testAwaitAdvance6() {
+        final Phaser phaser = new Phaser(3);
+        final CountDownLatch pleaseForceTermination = new CountDownLatch(2);
+        final List<Thread> threads = new ArrayList<Thread>();
+        for (int i = 0; i < 2; i++) {
+            Runnable r = new CheckedRunnable() {
+                public void realRun() {
+                    assertEquals(0, phaser.arrive());
+                    pleaseForceTermination.countDown();
+                    assertTrue(phaser.awaitAdvance(0) < 0);
+                    assertTrue(phaser.isTerminated());
+                    assertTrue(phaser.getPhase() < 0);
+                    assertEquals(0, phaser.getPhase() + Integer.MIN_VALUE);
+                    assertEquals(3, phaser.getRegisteredParties());
+                }};
+            threads.add(newStartedThread(r));
+        }
+        await(pleaseForceTermination);
+        phaser.forceTermination();
+        assertTrue(phaser.isTerminated());
+        assertEquals(0, phaser.getPhase() + Integer.MIN_VALUE);
+        for (Thread thread : threads)
+            awaitTermination(thread);
+        assertEquals(3, phaser.getRegisteredParties());
+    }
+
+    /**
+     * arriveAndAwaitAdvance throws IllegalStateException with no
+     * unarrived parties
+     */
+    public void testArriveAndAwaitAdvance1() {
+        Phaser phaser = new Phaser();
+        try {
+            phaser.arriveAndAwaitAdvance();
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * arriveAndAwaitAdvance waits for all threads to arrive, the
+     * number of arrived parties is the same number that is accounted
+     * for when the main thread awaitsAdvance
+     */
+    public void testArriveAndAwaitAdvance3() {
+        final Phaser phaser = new Phaser(1);
+        final int THREADS = 3;
+        final CountDownLatch pleaseArrive = new CountDownLatch(THREADS);
+        final List<Thread> threads = new ArrayList<Thread>();
+        for (int i = 0; i < THREADS; i++)
+            threads.add(newStartedThread(new CheckedRunnable() {
+                public void realRun() {
+                    assertEquals(0, phaser.register());
+                    pleaseArrive.countDown();
+                    assertEquals(1, phaser.arriveAndAwaitAdvance());
+                }}));
+
+        await(pleaseArrive);
+        long startTime = System.nanoTime();
+        while (phaser.getArrivedParties() < THREADS)
+            Thread.yield();
+        assertEquals(THREADS, phaser.getArrivedParties());
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        for (Thread thread : threads)
+            waitForThreadToEnterWaitState(thread, SHORT_DELAY_MS);
+        for (Thread thread : threads)
+            assertTrue(thread.isAlive());
+        assertState(phaser, 0, THREADS + 1, 1);
+        phaser.arriveAndAwaitAdvance();
+        for (Thread thread : threads)
+            awaitTermination(thread);
+        assertState(phaser, 1, THREADS + 1, THREADS + 1);
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/PriorityBlockingQueueTest.java b/jdk/test/java/util/concurrent/tck/PriorityBlockingQueueTest.java
new file mode 100644
index 00000000000..c586837720a
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/PriorityBlockingQueueTest.java
@@ -0,0 +1,764 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.PriorityBlockingQueue;
+
+import junit.framework.Test;
+
+public class PriorityBlockingQueueTest extends JSR166TestCase {
+
+    public static class Generic extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new PriorityBlockingQueue();
+        }
+    }
+
+    public static class InitialCapacity extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new PriorityBlockingQueue(SIZE);
+        }
+    }
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return newTestSuite(PriorityBlockingQueueTest.class,
+                            new Generic().testSuite(),
+                            new InitialCapacity().testSuite());
+    }
+
+    /** Sample Comparator */
+    static class MyReverseComparator implements Comparator {
+        public int compare(Object x, Object y) {
+            return ((Comparable)y).compareTo(x);
+        }
+    }
+
+    /**
+     * Returns a new queue of given size containing consecutive
+     * Integers 0 ... n.
+     */
+    private PriorityBlockingQueue<Integer> populatedQueue(int n) {
+        PriorityBlockingQueue<Integer> q =
+            new PriorityBlockingQueue<Integer>(n);
+        assertTrue(q.isEmpty());
+        for (int i = n - 1; i >= 0; i -= 2)
+            assertTrue(q.offer(new Integer(i)));
+        for (int i = (n & 1); i < n; i += 2)
+            assertTrue(q.offer(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+        assertEquals(n, q.size());
+        return q;
+    }
+
+    /**
+     * A new queue has unbounded capacity
+     */
+    public void testConstructor1() {
+        assertEquals(Integer.MAX_VALUE,
+                     new PriorityBlockingQueue(SIZE).remainingCapacity());
+    }
+
+    /**
+     * Constructor throws IAE if capacity argument nonpositive
+     */
+    public void testConstructor2() {
+        try {
+            new PriorityBlockingQueue(0);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            new PriorityBlockingQueue(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NPE
+     */
+    public void testConstructor4() {
+        Collection<Integer> elements = Arrays.asList(new Integer[SIZE]);
+        try {
+            new PriorityBlockingQueue(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws NPE
+     */
+    public void testConstructor5() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = i;
+        Collection<Integer> elements = Arrays.asList(ints);
+        try {
+            new PriorityBlockingQueue(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = i;
+        PriorityBlockingQueue q = new PriorityBlockingQueue(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * The comparator used in constructor is used
+     */
+    public void testConstructor7() {
+        MyReverseComparator cmp = new MyReverseComparator();
+        PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE, cmp);
+        assertEquals(cmp, q.comparator());
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        q.addAll(Arrays.asList(ints));
+        for (int i = SIZE - 1; i >= 0; --i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        PriorityBlockingQueue q = new PriorityBlockingQueue(2);
+        assertTrue(q.isEmpty());
+        assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        q.add(two);
+        q.remove();
+        q.remove();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * remainingCapacity() always returns Integer.MAX_VALUE
+     */
+    public void testRemainingCapacity() {
+        BlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+            assertEquals(SIZE - i, q.size());
+            assertEquals(i, q.remove());
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+            assertEquals(i, q.size());
+            assertTrue(q.add(i));
+        }
+    }
+
+    /**
+     * Offer of comparable element succeeds
+     */
+    public void testOffer() {
+        PriorityBlockingQueue q = new PriorityBlockingQueue(1);
+        assertTrue(q.offer(zero));
+        assertTrue(q.offer(one));
+    }
+
+    /**
+     * Offer of non-Comparable throws CCE
+     */
+    public void testOfferNonComparable() {
+        PriorityBlockingQueue q = new PriorityBlockingQueue(1);
+        try {
+            q.offer(new Object());
+            q.offer(new Object());
+            shouldThrow();
+        } catch (ClassCastException success) {}
+    }
+
+    /**
+     * add of comparable succeeds
+     */
+    public void testAdd() {
+        PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            assertTrue(q.add(new Integer(i)));
+        }
+    }
+
+    /**
+     * addAll(this) throws IAE
+     */
+    public void testAddAllSelf() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        try {
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = SIZE - 1; i >= 0; --i)
+            ints[i] = new Integer(i);
+        PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE);
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * all elements successfully put are contained
+     */
+    public void testPut() {
+        PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            Integer x = new Integer(i);
+            q.put(x);
+            assertTrue(q.contains(x));
+        }
+        assertEquals(SIZE, q.size());
+    }
+
+    /**
+     * put doesn't block waiting for take
+     */
+    public void testPutWithTake() throws InterruptedException {
+        final PriorityBlockingQueue q = new PriorityBlockingQueue(2);
+        final int size = 4;
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                for (int i = 0; i < size; i++)
+                    q.put(new Integer(0));
+            }});
+
+        awaitTermination(t);
+        assertEquals(size, q.size());
+        q.take();
+    }
+
+    /**
+     * timed offer does not time out
+     */
+    public void testTimedOffer() throws InterruptedException {
+        final PriorityBlockingQueue q = new PriorityBlockingQueue(2);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                q.put(new Integer(0));
+                q.put(new Integer(0));
+                assertTrue(q.offer(new Integer(0), SHORT_DELAY_MS, MILLISECONDS));
+                assertTrue(q.offer(new Integer(0), LONG_DELAY_MS, MILLISECONDS));
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * take retrieves elements in priority order
+     */
+    public void testTake() throws InterruptedException {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.take());
+        }
+    }
+
+    /**
+     * Take removes existing elements until empty, then blocks interruptibly
+     */
+    public void testBlockingTake() throws InterruptedException {
+        final PriorityBlockingQueue q = populatedQueue(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(i, q.take());
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll());
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * timed poll with zero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll0() throws InterruptedException {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll(0, MILLISECONDS));
+        }
+        assertNull(q.poll(0, MILLISECONDS));
+    }
+
+    /**
+     * timed poll with nonzero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll() throws InterruptedException {
+        PriorityBlockingQueue<Integer> q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            long startTime = System.nanoTime();
+            assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+        long startTime = System.nanoTime();
+        assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        checkEmpty(q);
+    }
+
+    /**
+     * Interrupted timed poll throws InterruptedException instead of
+     * returning timeout status
+     */
+    public void testInterruptedTimedPoll() throws InterruptedException {
+        final BlockingQueue<Integer> q = populatedQueue(SIZE);
+        final CountDownLatch aboutToWait = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
+                }
+                aboutToWait.countDown();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {
+                    assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+                }
+            }});
+
+        aboutToWait.await();
+        waitForThreadToEnterWaitState(t, LONG_DELAY_MS);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * peek returns next element, or null if empty
+     */
+    public void testPeek() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peek());
+            assertEquals(i, q.poll());
+            assertTrue(q.peek() == null ||
+                       !q.peek().equals(i));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * element returns next element, or throws NSEE if empty
+     */
+    public void testElement() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.element());
+            assertEquals(i, q.poll());
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.poll();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(one));
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        PriorityBlockingQueue p = new PriorityBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        PriorityBlockingQueue p = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            PriorityBlockingQueue q = populatedQueue(SIZE);
+            PriorityBlockingQueue p = populatedQueue(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer x = (Integer)(p.remove());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    /**
+     * toArray contains all elements
+     */
+    public void testToArray() throws InterruptedException {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        Object[] o = q.toArray();
+        Arrays.sort(o);
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.take());
+    }
+
+    /**
+     * toArray(a) contains all elements
+     */
+    public void testToArray2() throws InterruptedException {
+        PriorityBlockingQueue<Integer> q = populatedQueue(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        Arrays.sort(ints);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.take());
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray1_BadArg() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        try {
+            q.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        Iterator it = q.iterator();
+        int i;
+        for (i = 0; it.hasNext(); i++)
+            assertTrue(q.contains(it.next()));
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty collection has no elements
+     */
+    public void testEmptyIterator() {
+        assertIteratorExhausted(new PriorityBlockingQueue().iterator());
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final PriorityBlockingQueue q = new PriorityBlockingQueue(3);
+        q.add(new Integer(2));
+        q.add(new Integer(1));
+        q.add(new Integer(3));
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertEquals(it.next(), new Integer(2));
+        assertEquals(it.next(), new Integer(3));
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * timed poll transfers elements across Executor tasks
+     */
+    public void testPollInExecutor() {
+        final PriorityBlockingQueue q = new PriorityBlockingQueue(2);
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        final ExecutorService executor = Executors.newFixedThreadPool(2);
+        try (PoolCleaner cleaner = cleaner(executor)) {
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertNull(q.poll());
+                    threadsStarted.await();
+                    assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
+                    checkEmpty(q);
+                }});
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadsStarted.await();
+                    q.put(one);
+                }});
+        }
+    }
+
+    /**
+     * A deserialized serialized queue has same elements
+     */
+    public void testSerialization() throws Exception {
+        Queue x = populatedQueue(SIZE);
+        Queue y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(), y.remove());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * drainTo(c) empties queue into another collection c
+     */
+    public void testDrainTo() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(SIZE, l.size());
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(l.get(i), new Integer(i));
+        q.add(zero);
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(zero));
+        assertTrue(q.contains(one));
+        l.clear();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(2, l.size());
+        for (int i = 0; i < 2; ++i)
+            assertEquals(l.get(i), new Integer(i));
+    }
+
+    /**
+     * drainTo empties queue
+     */
+    public void testDrainToWithActivePut() throws InterruptedException {
+        final PriorityBlockingQueue q = populatedQueue(SIZE);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                q.put(new Integer(SIZE + 1));
+            }});
+
+        t.start();
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertTrue(l.size() >= SIZE);
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(l.get(i), new Integer(i));
+        t.join();
+        assertTrue(q.size() + l.size() >= SIZE);
+    }
+
+    /**
+     * drainTo(c, n) empties first min(n, size) elements of queue into c
+     */
+    public void testDrainToN() {
+        PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE * 2);
+        for (int i = 0; i < SIZE + 2; ++i) {
+            for (int j = 0; j < SIZE; j++)
+                assertTrue(q.offer(new Integer(j)));
+            ArrayList l = new ArrayList();
+            q.drainTo(l, i);
+            int k = (i < SIZE) ? i : SIZE;
+            assertEquals(k, l.size());
+            assertEquals(SIZE - k, q.size());
+            for (int j = 0; j < k; ++j)
+                assertEquals(l.get(j), new Integer(j));
+            do {} while (q.poll() != null);
+        }
+    }
+
+    /**
+     * remove(null), contains(null) always return false
+     */
+    public void testNeverContainsNull() {
+        Collection<?>[] qs = {
+            new PriorityBlockingQueue<Object>(),
+            populatedQueue(2),
+        };
+
+        for (Collection<?> q : qs) {
+            assertFalse(q.contains(null));
+            assertFalse(q.remove(null));
+        }
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/PriorityQueueTest.java b/jdk/test/java/util/concurrent/tck/PriorityQueueTest.java
new file mode 100644
index 00000000000..edb2379e2ec
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/PriorityQueueTest.java
@@ -0,0 +1,528 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.PriorityQueue;
+import java.util.Queue;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class PriorityQueueTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(PriorityQueueTest.class);
+    }
+
+    static class MyReverseComparator implements Comparator {
+        public int compare(Object x, Object y) {
+            return ((Comparable)y).compareTo(x);
+        }
+    }
+
+    /**
+     * Returns a new queue of given size containing consecutive
+     * Integers 0 ... n.
+     */
+    private PriorityQueue<Integer> populatedQueue(int n) {
+        PriorityQueue<Integer> q = new PriorityQueue<Integer>(n);
+        assertTrue(q.isEmpty());
+        for (int i = n - 1; i >= 0; i -= 2)
+            assertTrue(q.offer(new Integer(i)));
+        for (int i = (n & 1); i < n; i += 2)
+            assertTrue(q.offer(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(n, q.size());
+        return q;
+    }
+
+    /**
+     * A new queue has unbounded capacity
+     */
+    public void testConstructor1() {
+        assertEquals(0, new PriorityQueue(SIZE).size());
+    }
+
+    /**
+     * Constructor throws IAE if capacity argument nonpositive
+     */
+    public void testConstructor2() {
+        try {
+            new PriorityQueue(0);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            new PriorityQueue((Collection)null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NPE
+     */
+    public void testConstructor4() {
+        try {
+            new PriorityQueue(Arrays.asList(new Integer[SIZE]));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws NPE
+     */
+    public void testConstructor5() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            new PriorityQueue(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        PriorityQueue q = new PriorityQueue(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * The comparator used in constructor is used
+     */
+    public void testConstructor7() {
+        MyReverseComparator cmp = new MyReverseComparator();
+        PriorityQueue q = new PriorityQueue(SIZE, cmp);
+        assertEquals(cmp, q.comparator());
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        q.addAll(Arrays.asList(ints));
+        for (int i = SIZE - 1; i >= 0; --i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        PriorityQueue q = new PriorityQueue(2);
+        assertTrue(q.isEmpty());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.add(new Integer(2));
+        q.remove();
+        q.remove();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testSize() {
+        PriorityQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i, q.size());
+            q.remove();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * offer(null) throws NPE
+     */
+    public void testOfferNull() {
+        PriorityQueue q = new PriorityQueue(1);
+        try {
+            q.offer(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * add(null) throws NPE
+     */
+    public void testAddNull() {
+        PriorityQueue q = new PriorityQueue(1);
+        try {
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Offer of comparable element succeeds
+     */
+    public void testOffer() {
+        PriorityQueue q = new PriorityQueue(1);
+        assertTrue(q.offer(zero));
+        assertTrue(q.offer(one));
+    }
+
+    /**
+     * Offer of non-Comparable throws CCE
+     */
+    public void testOfferNonComparable() {
+        PriorityQueue q = new PriorityQueue(1);
+        try {
+            q.offer(new Object());
+            q.offer(new Object());
+            shouldThrow();
+        } catch (ClassCastException success) {}
+    }
+
+    /**
+     * add of comparable succeeds
+     */
+    public void testAdd() {
+        PriorityQueue q = new PriorityQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            assertTrue(q.add(new Integer(i)));
+        }
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        PriorityQueue q = new PriorityQueue(1);
+        try {
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testAddAll2() {
+        PriorityQueue q = new PriorityQueue(SIZE);
+        try {
+            q.addAll(Arrays.asList(new Integer[SIZE]));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        PriorityQueue q = new PriorityQueue(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(SIZE - 1 - i);
+        PriorityQueue q = new PriorityQueue(SIZE);
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(new Integer(i), q.poll());
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        PriorityQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll());
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * peek returns next element, or null if empty
+     */
+    public void testPeek() {
+        PriorityQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peek());
+            assertEquals(i, q.poll());
+            assertTrue(q.peek() == null ||
+                       !q.peek().equals(i));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * element returns next element, or throws NSEE if empty
+     */
+    public void testElement() {
+        PriorityQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.element());
+            assertEquals(i, q.poll());
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        PriorityQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testRemoveElement() {
+        PriorityQueue q = populatedQueue(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertTrue(q.contains(i - 1));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertFalse(q.remove(i + 1));
+            assertFalse(q.contains(i + 1));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        PriorityQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.poll();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        PriorityQueue q = populatedQueue(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        PriorityQueue q = populatedQueue(SIZE);
+        PriorityQueue p = new PriorityQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        PriorityQueue q = populatedQueue(SIZE);
+        PriorityQueue p = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            PriorityQueue q = populatedQueue(SIZE);
+            PriorityQueue p = populatedQueue(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer x = (Integer)(p.remove());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    /**
+     * toArray contains all elements
+     */
+    public void testToArray() {
+        PriorityQueue q = populatedQueue(SIZE);
+        Object[] o = q.toArray();
+        Arrays.sort(o);
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.poll());
+    }
+
+    /**
+     * toArray(a) contains all elements
+     */
+    public void testToArray2() {
+        PriorityQueue<Integer> q = populatedQueue(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        Arrays.sort(ints);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.poll());
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() {
+        PriorityQueue q = populatedQueue(SIZE);
+        Iterator it = q.iterator();
+        int i;
+        for (i = 0; it.hasNext(); i++)
+            assertTrue(q.contains(it.next()));
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty collection has no elements
+     */
+    public void testEmptyIterator() {
+        assertIteratorExhausted(new PriorityQueue().iterator());
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final PriorityQueue q = new PriorityQueue(3);
+        q.add(new Integer(2));
+        q.add(new Integer(1));
+        q.add(new Integer(3));
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertEquals(it.next(), new Integer(2));
+        assertEquals(it.next(), new Integer(3));
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        PriorityQueue q = populatedQueue(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized queue has same elements
+     */
+    public void testSerialization() throws Exception {
+        Queue x = populatedQueue(SIZE);
+        Queue y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(), y.remove());
+        }
+        assertTrue(y.isEmpty());
+    }
+}
diff --git a/jdk/test/java/util/concurrent/tck/RecursiveActionTest.java b/jdk/test/java/util/concurrent/tck/RecursiveActionTest.java
new file mode 100644
index 00000000000..9ce68c34166
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/RecursiveActionTest.java
@@ -0,0 +1,1272 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.ForkJoinWorkerThread;
+import java.util.concurrent.RecursiveAction;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeoutException;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class RecursiveActionTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(RecursiveActionTest.class);
+    }
+
+    private static ForkJoinPool mainPool() {
+        return new ForkJoinPool();
+    }
+
+    private static ForkJoinPool singletonPool() {
+        return new ForkJoinPool(1);
+    }
+
+    private static ForkJoinPool asyncSingletonPool() {
+        return new ForkJoinPool(1,
+                                ForkJoinPool.defaultForkJoinWorkerThreadFactory,
+                                null, true);
+    }
+
+    private void testInvokeOnPool(ForkJoinPool pool, RecursiveAction a) {
+        try (PoolCleaner cleaner = cleaner(pool)) {
+            checkNotDone(a);
+
+            assertNull(pool.invoke(a));
+
+            checkCompletedNormally(a);
+        }
+    }
+
+    void checkNotDone(RecursiveAction a) {
+        assertFalse(a.isDone());
+        assertFalse(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertFalse(a.isCancelled());
+        assertNull(a.getException());
+        assertNull(a.getRawResult());
+
+        if (! ForkJoinTask.inForkJoinPool()) {
+            Thread.currentThread().interrupt();
+            try {
+                a.get();
+                shouldThrow();
+            } catch (InterruptedException success) {
+            } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+            Thread.currentThread().interrupt();
+            try {
+                a.get(5L, SECONDS);
+                shouldThrow();
+            } catch (InterruptedException success) {
+            } catch (Throwable fail) { threadUnexpectedException(fail); }
+        }
+
+        try {
+            a.get(0L, SECONDS);
+            shouldThrow();
+        } catch (TimeoutException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCompletedNormally(RecursiveAction a) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertTrue(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertNull(a.getException());
+        assertNull(a.getRawResult());
+        assertNull(a.join());
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+        try {
+            assertNull(a.get());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        try {
+            assertNull(a.get(5L, SECONDS));
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCancelled(RecursiveAction a) {
+        assertTrue(a.isDone());
+        assertTrue(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertTrue(a.getException() instanceof CancellationException);
+        assertNull(a.getRawResult());
+
+        try {
+            a.join();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCompletedAbnormally(RecursiveAction a, Throwable t) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertSame(t.getClass(), a.getException().getClass());
+        assertNull(a.getRawResult());
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+
+        try {
+            a.join();
+            shouldThrow();
+        } catch (Throwable expected) {
+            assertSame(expected.getClass(), t.getClass());
+        }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    public static final class FJException extends RuntimeException {
+        public FJException() { super(); }
+        public FJException(Throwable cause) { super(cause); }
+    }
+
+    // A simple recursive action for testing
+    final class FibAction extends CheckedRecursiveAction {
+        final int number;
+        int result;
+        FibAction(int n) { number = n; }
+        protected void realCompute() {
+            int n = number;
+            if (n <= 1)
+                result = n;
+            else {
+                FibAction f1 = new FibAction(n - 1);
+                FibAction f2 = new FibAction(n - 2);
+                invokeAll(f1, f2);
+                result = f1.result + f2.result;
+            }
+        }
+    }
+
+    // A recursive action failing in base case
+    static final class FailingFibAction extends RecursiveAction {
+        final int number;
+        int result;
+        FailingFibAction(int n) { number = n; }
+        public void compute() {
+            int n = number;
+            if (n <= 1)
+                throw new FJException();
+            else {
+                FailingFibAction f1 = new FailingFibAction(n - 1);
+                FailingFibAction f2 = new FailingFibAction(n - 2);
+                invokeAll(f1, f2);
+                result = f1.result + f2.result;
+            }
+        }
+    }
+
+    /**
+     * invoke returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks. getRawResult of a RecursiveAction returns null;
+     */
+    public void testInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertNull(f.invoke());
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks
+     */
+    public void testQuietlyInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                f.quietlyInvoke();
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join of a forked task returns when task completes
+     */
+    public void testForkJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertNull(f.join());
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join/quietlyJoin of a forked task succeeds in the presence of interrupts
+     */
+    public void testJoinIgnoresInterrupts() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                final Thread myself = Thread.currentThread();
+
+                // test join()
+                assertSame(f, f.fork());
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                assertNull(f.join());
+                Thread.interrupted();
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+
+                f = new FibAction(8);
+                f.cancel(true);
+                assertSame(f, f.fork());
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    Thread.interrupted();
+                    checkCancelled(f);
+                }
+
+                f = new FibAction(8);
+                f.completeExceptionally(new FJException());
+                assertSame(f, f.fork());
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    Thread.interrupted();
+                    checkCompletedAbnormally(f, success);
+                }
+
+                // test quietlyJoin()
+                f = new FibAction(8);
+                assertSame(f, f.fork());
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                f.quietlyJoin();
+                Thread.interrupted();
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+
+                f = new FibAction(8);
+                f.cancel(true);
+                assertSame(f, f.fork());
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                f.quietlyJoin();
+                Thread.interrupted();
+                checkCancelled(f);
+
+                f = new FibAction(8);
+                f.completeExceptionally(new FJException());
+                assertSame(f, f.fork());
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                f.quietlyJoin();
+                Thread.interrupted();
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(mainPool(), a);
+        a.reinitialize();
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * join/quietlyJoin of a forked task when not in ForkJoinPool
+     * succeeds in the presence of interrupts
+     */
+    public void testJoinIgnoresInterruptsOutsideForkJoinPool() {
+        final SynchronousQueue<FibAction[]> sq =
+            new SynchronousQueue<FibAction[]>();
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws InterruptedException {
+                FibAction[] fibActions = new FibAction[6];
+                for (int i = 0; i < fibActions.length; i++)
+                    fibActions[i] = new FibAction(8);
+
+                fibActions[1].cancel(false);
+                fibActions[2].completeExceptionally(new FJException());
+                fibActions[4].cancel(true);
+                fibActions[5].completeExceptionally(new FJException());
+
+                for (int i = 0; i < fibActions.length; i++)
+                    fibActions[i].fork();
+
+                sq.put(fibActions);
+
+                helpQuiesce();
+            }};
+
+        Runnable r = new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                FibAction[] fibActions = sq.take();
+                FibAction f;
+                final Thread myself = Thread.currentThread();
+
+                // test join() ------------
+
+                f = fibActions[0];
+                assertFalse(ForkJoinTask.inForkJoinPool());
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                assertNull(f.join());
+                assertTrue(Thread.interrupted());
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+
+                f = fibActions[1];
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    assertTrue(Thread.interrupted());
+                    checkCancelled(f);
+                }
+
+                f = fibActions[2];
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    assertTrue(Thread.interrupted());
+                    checkCompletedAbnormally(f, success);
+                }
+
+                // test quietlyJoin() ---------
+
+                f = fibActions[3];
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                f.quietlyJoin();
+                assertTrue(Thread.interrupted());
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+
+                f = fibActions[4];
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                f.quietlyJoin();
+                assertTrue(Thread.interrupted());
+                checkCancelled(f);
+
+                f = fibActions[5];
+                myself.interrupt();
+                assertTrue(myself.isInterrupted());
+                f.quietlyJoin();
+                assertTrue(Thread.interrupted());
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+
+        Thread t;
+
+        t = newStartedThread(r);
+        testInvokeOnPool(mainPool(), a);
+        awaitTermination(t);
+
+        a.reinitialize();
+        t = newStartedThread(r);
+        testInvokeOnPool(singletonPool(), a);
+        awaitTermination(t);
+    }
+
+    /**
+     * get of a forked task returns when task completes
+     */
+    public void testForkGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertNull(f.get());
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get of a forked task returns when task completes
+     */
+    public void testForkTimedGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertNull(f.get(5L, SECONDS));
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get with null time unit throws NPE
+     */
+    public void testForkTimedGetNPE() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, null);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes
+     */
+    public void testForkQuietlyJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * helpQuiesce returns when tasks are complete.
+     * getQueuedTaskCount returns 0 when quiescent
+     */
+    public void testForkHelpQuiesce() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                helpQuiesce();
+                while (!f.isDone()) // wait out race
+                    ;
+                assertEquals(21, f.result);
+                assertEquals(0, getQueuedTaskCount());
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task completes abnormally
+     */
+    public void testAbnormalInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes abnormally
+     */
+    public void testAbnormalQuietlyInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                f.quietlyInvoke();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingFibAction f = new FailingFibAction(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkTimedGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingFibAction f = new FailingFibAction(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, SECONDS);
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes abnormally
+     */
+    public void testAbnormalForkQuietlyJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task cancelled
+     */
+    public void testCancelledInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertTrue(f.cancel(true));
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FibAction f = new FibAction(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkTimedGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FibAction f = new FibAction(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, SECONDS);
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task cancelled
+     */
+    public void testCancelledForkQuietlyJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                checkCancelled(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * getPool of executing task returns its pool
+     */
+    public void testGetPool() {
+        final ForkJoinPool mainPool = mainPool();
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertSame(mainPool, getPool());
+            }};
+        testInvokeOnPool(mainPool, a);
+    }
+
+    /**
+     * getPool of non-FJ task returns null
+     */
+    public void testGetPool2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertNull(getPool());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * inForkJoinPool of executing task returns true
+     */
+    public void testInForkJoinPool() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertTrue(inForkJoinPool());
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * inForkJoinPool of non-FJ task returns false
+     */
+    public void testInForkJoinPool2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertFalse(inForkJoinPool());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * getPool of current thread in pool returns its pool
+     */
+    public void testWorkerGetPool() {
+        final ForkJoinPool mainPool = mainPool();
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                ForkJoinWorkerThread w =
+                    (ForkJoinWorkerThread) Thread.currentThread();
+                assertSame(mainPool, w.getPool());
+            }};
+        testInvokeOnPool(mainPool, a);
+    }
+
+    /**
+     * getPoolIndex of current thread in pool returns 0 <= value < poolSize
+     */
+    public void testWorkerGetPoolIndex() {
+        final ForkJoinPool mainPool = mainPool();
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                ForkJoinWorkerThread w =
+                    (ForkJoinWorkerThread) Thread.currentThread();
+                assertTrue(w.getPoolIndex() >= 0);
+                // pool size can shrink after assigning index, so cannot check
+                // assertTrue(w.getPoolIndex() < mainPool.getPoolSize());
+            }};
+        testInvokeOnPool(mainPool, a);
+    }
+
+    /**
+     * setRawResult(null) succeeds
+     */
+    public void testSetRawResult() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                setRawResult(null);
+                assertNull(getRawResult());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * A reinitialized normally completed task may be re-invoked
+     */
+    public void testReinitialize() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                checkNotDone(f);
+
+                for (int i = 0; i < 3; i++) {
+                    assertNull(f.invoke());
+                    assertEquals(21, f.result);
+                    checkCompletedNormally(f);
+                    f.reinitialize();
+                    checkNotDone(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * A reinitialized abnormally completed task may be re-invoked
+     */
+    public void testReinitializeAbnormal() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                checkNotDone(f);
+
+                for (int i = 0; i < 3; i++) {
+                    try {
+                        f.invoke();
+                        shouldThrow();
+                    } catch (FJException success) {
+                        checkCompletedAbnormally(f, success);
+                    }
+                    f.reinitialize();
+                    checkNotDone(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invoke task throws exception after invoking completeExceptionally
+     */
+    public void testCompleteExceptionally() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                f.completeExceptionally(new FJException());
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invoke task suppresses execution invoking complete
+     */
+    public void testComplete() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                f.complete(null);
+                assertNull(f.invoke());
+                assertEquals(0, f.result);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) invokes all task arguments
+     */
+    public void testInvokeAll2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FibAction g = new FibAction(9);
+                invokeAll(f, g);
+                checkCompletedNormally(f);
+                assertEquals(21, f.result);
+                checkCompletedNormally(g);
+                assertEquals(34, g.result);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument invokes task
+     */
+    public void testInvokeAll1() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                invokeAll(f);
+                checkCompletedNormally(f);
+                assertEquals(21, f.result);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument invokes tasks
+     */
+    public void testInvokeAll3() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FibAction g = new FibAction(9);
+                FibAction h = new FibAction(7);
+                invokeAll(f, g, h);
+                assertTrue(f.isDone());
+                assertTrue(g.isDone());
+                assertTrue(h.isDone());
+                checkCompletedNormally(f);
+                assertEquals(21, f.result);
+                checkCompletedNormally(g);
+                assertEquals(34, g.result);
+                checkCompletedNormally(g);
+                assertEquals(13, h.result);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(collection) invokes all tasks in the collection
+     */
+    public void testInvokeAllCollection() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FibAction g = new FibAction(9);
+                FibAction h = new FibAction(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                invokeAll(set);
+                assertTrue(f.isDone());
+                assertTrue(g.isDone());
+                assertTrue(h.isDone());
+                checkCompletedNormally(f);
+                assertEquals(21, f.result);
+                checkCompletedNormally(g);
+                assertEquals(34, g.result);
+                checkCompletedNormally(g);
+                assertEquals(13, h.result);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with any null task throws NPE
+     */
+    public void testInvokeAllNPE() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FibAction g = new FibAction(9);
+                FibAction h = null;
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) throw exception if any task does
+     */
+    public void testAbnormalInvokeAll2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FailingFibAction g = new FailingFibAction(9);
+                try {
+                    invokeAll(f, g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument throws exception if task does
+     */
+    public void testAbnormalInvokeAll1() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction g = new FailingFibAction(9);
+                try {
+                    invokeAll(g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument throws exception if any task does
+     */
+    public void testAbnormalInvokeAll3() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FailingFibAction g = new FailingFibAction(9);
+                FibAction h = new FibAction(7);
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(collection) throws exception if any task does
+     */
+    public void testAbnormalInvokeAllCollection() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                FibAction g = new FibAction(9);
+                FibAction h = new FibAction(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                try {
+                    invokeAll(set);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * tryUnfork returns true for most recent unexecuted task,
+     * and suppresses execution
+     */
+    public void testTryUnfork() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction g = new FibAction(9);
+                assertSame(g, g.fork());
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertTrue(f.tryUnfork());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * getSurplusQueuedTaskCount returns > 0 when
+     * there are more tasks than threads
+     */
+    public void testGetSurplusQueuedTaskCount() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction h = new FibAction(7);
+                assertSame(h, h.fork());
+                FibAction g = new FibAction(9);
+                assertSame(g, g.fork());
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertTrue(getSurplusQueuedTaskCount() > 0);
+                helpQuiesce();
+                assertEquals(0, getSurplusQueuedTaskCount());
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * peekNextLocalTask returns most recent unexecuted task.
+     */
+    public void testPeekNextLocalTask() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction g = new FibAction(9);
+                assertSame(g, g.fork());
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertSame(f, peekNextLocalTask());
+                assertNull(f.join());
+                checkCompletedNormally(f);
+                helpQuiesce();
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * pollNextLocalTask returns most recent unexecuted task
+     * without executing it
+     */
+    public void testPollNextLocalTask() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction g = new FibAction(9);
+                assertSame(g, g.fork());
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertSame(f, pollNextLocalTask());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * pollTask returns an unexecuted task without executing it
+     */
+    public void testPollTask() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction g = new FibAction(9);
+                assertSame(g, g.fork());
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertSame(f, pollTask());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * peekNextLocalTask returns least recent unexecuted task in async mode
+     */
+    public void testPeekNextLocalTaskAsync() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction g = new FibAction(9);
+                assertSame(g, g.fork());
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertSame(g, peekNextLocalTask());
+                assertNull(f.join());
+                helpQuiesce();
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    /**
+     * pollNextLocalTask returns least recent unexecuted task without
+     * executing it, in async mode
+     */
+    public void testPollNextLocalTaskAsync() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction g = new FibAction(9);
+                assertSame(g, g.fork());
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertSame(g, pollNextLocalTask());
+                helpQuiesce();
+                checkCompletedNormally(f);
+                checkNotDone(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    /**
+     * pollTask returns an unexecuted task without executing it, in
+     * async mode
+     */
+    public void testPollTaskAsync() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction g = new FibAction(9);
+                assertSame(g, g.fork());
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertSame(g, pollTask());
+                helpQuiesce();
+                checkCompletedNormally(f);
+                checkNotDone(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    /** Demo from RecursiveAction javadoc */
+    static class SortTask extends RecursiveAction {
+        final long[] array; final int lo, hi;
+        SortTask(long[] array, int lo, int hi) {
+            this.array = array; this.lo = lo; this.hi = hi;
+        }
+        SortTask(long[] array) { this(array, 0, array.length); }
+        protected void compute() {
+            if (hi - lo < THRESHOLD)
+                sortSequentially(lo, hi);
+            else {
+                int mid = (lo + hi) >>> 1;
+                invokeAll(new SortTask(array, lo, mid),
+                          new SortTask(array, mid, hi));
+                merge(lo, mid, hi);
+            }
+        }
+        // implementation details follow:
+        static final int THRESHOLD = 100;
+        void sortSequentially(int lo, int hi) {
+            Arrays.sort(array, lo, hi);
+        }
+        void merge(int lo, int mid, int hi) {
+            long[] buf = Arrays.copyOfRange(array, lo, mid);
+            for (int i = 0, j = lo, k = mid; i < buf.length; j++)
+                array[j] = (k == hi || buf[i] < array[k]) ?
+                    buf[i++] : array[k++];
+        }
+    }
+
+    /**
+     * SortTask demo works as advertised
+     */
+    public void testSortTaskDemo() {
+        ThreadLocalRandom rnd = ThreadLocalRandom.current();
+        long[] array = new long[1007];
+        for (int i = 0; i < array.length; i++)
+            array[i] = rnd.nextLong();
+        long[] arrayClone = array.clone();
+        testInvokeOnPool(mainPool(), new SortTask(array));
+        Arrays.sort(arrayClone);
+        assertTrue(Arrays.equals(array, arrayClone));
+    }
+}
diff --git a/jdk/test/java/util/concurrent/tck/RecursiveTaskTest.java b/jdk/test/java/util/concurrent/tck/RecursiveTaskTest.java
new file mode 100644
index 00000000000..ce13daa8e7b
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/RecursiveTaskTest.java
@@ -0,0 +1,1053 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.util.HashSet;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.RecursiveTask;
+import java.util.concurrent.TimeoutException;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class RecursiveTaskTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(RecursiveTaskTest.class);
+    }
+
+    private static ForkJoinPool mainPool() {
+        return new ForkJoinPool();
+    }
+
+    private static ForkJoinPool singletonPool() {
+        return new ForkJoinPool(1);
+    }
+
+    private static ForkJoinPool asyncSingletonPool() {
+        return new ForkJoinPool(1,
+                                ForkJoinPool.defaultForkJoinWorkerThreadFactory,
+                                null, true);
+    }
+
+    private <T> T testInvokeOnPool(ForkJoinPool pool, RecursiveTask<T> a) {
+        try (PoolCleaner cleaner = cleaner(pool)) {
+            checkNotDone(a);
+
+            T result = pool.invoke(a);
+
+            checkCompletedNormally(a, result);
+            return result;
+        }
+    }
+
+    void checkNotDone(RecursiveTask a) {
+        assertFalse(a.isDone());
+        assertFalse(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertFalse(a.isCancelled());
+        assertNull(a.getException());
+        assertNull(a.getRawResult());
+
+        if (! ForkJoinTask.inForkJoinPool()) {
+            Thread.currentThread().interrupt();
+            try {
+                a.get();
+                shouldThrow();
+            } catch (InterruptedException success) {
+            } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+            Thread.currentThread().interrupt();
+            try {
+                a.get(5L, SECONDS);
+                shouldThrow();
+            } catch (InterruptedException success) {
+            } catch (Throwable fail) { threadUnexpectedException(fail); }
+        }
+
+        try {
+            a.get(0L, SECONDS);
+            shouldThrow();
+        } catch (TimeoutException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    <T> void checkCompletedNormally(RecursiveTask<T> a, T expected) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertTrue(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertNull(a.getException());
+        assertSame(expected, a.getRawResult());
+        assertSame(expected, a.join());
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+        try {
+            assertSame(expected, a.get());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        try {
+            assertSame(expected, a.get(5L, SECONDS));
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    /**
+     * Waits for the task to complete, and checks that when it does,
+     * it will have an Integer result equals to the given int.
+     */
+    void checkCompletesNormally(RecursiveTask<Integer> a, int expected) {
+        Integer r = a.join();
+        assertEquals(expected, (int) r);
+        checkCompletedNormally(a, r);
+    }
+
+    /**
+     * Like checkCompletesNormally, but verifies that the task has
+     * already completed.
+     */
+    void checkCompletedNormally(RecursiveTask<Integer> a, int expected) {
+        Integer r = a.getRawResult();
+        assertEquals(expected, (int) r);
+        checkCompletedNormally(a, r);
+    }
+
+    void checkCancelled(RecursiveTask a) {
+        assertTrue(a.isDone());
+        assertTrue(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertTrue(a.getException() instanceof CancellationException);
+        assertNull(a.getRawResult());
+
+        try {
+            a.join();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCompletedAbnormally(RecursiveTask a, Throwable t) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertSame(t.getClass(), a.getException().getClass());
+        assertNull(a.getRawResult());
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+
+        try {
+            a.join();
+            shouldThrow();
+        } catch (Throwable expected) {
+            assertSame(t.getClass(), expected.getClass());
+        }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    public static final class FJException extends RuntimeException {
+        public FJException() { super(); }
+    }
+
+    // An invalid return value for Fib
+    static final Integer NoResult = Integer.valueOf(-17);
+
+    // A simple recursive task for testing
+    final class FibTask extends CheckedRecursiveTask<Integer> {
+        final int number;
+        FibTask(int n) { number = n; }
+        public Integer realCompute() {
+            int n = number;
+            if (n <= 1)
+                return n;
+            FibTask f1 = new FibTask(n - 1);
+            f1.fork();
+            return (new FibTask(n - 2)).compute() + f1.join();
+        }
+
+        public void publicSetRawResult(Integer result) {
+            setRawResult(result);
+        }
+    }
+
+    // A recursive action failing in base case
+    final class FailingFibTask extends RecursiveTask<Integer> {
+        final int number;
+        int result;
+        FailingFibTask(int n) { number = n; }
+        public Integer compute() {
+            int n = number;
+            if (n <= 1)
+                throw new FJException();
+            FailingFibTask f1 = new FailingFibTask(n - 1);
+            f1.fork();
+            return (new FibTask(n - 2)).compute() + f1.join();
+        }
+    }
+
+    /**
+     * invoke returns value when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks. getRawResult of a completed non-null task
+     * returns value;
+     */
+    public void testInvoke() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                Integer r = f.invoke();
+                assertEquals(21, (int) r);
+                checkCompletedNormally(f, r);
+                return r;
+            }};
+        assertEquals(21, (int) testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks
+     */
+    public void testQuietlyInvoke() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                f.quietlyInvoke();
+                checkCompletedNormally(f, 21);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * join of a forked task returns when task completes
+     */
+    public void testForkJoin() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                Integer r = f.join();
+                assertEquals(21, (int) r);
+                checkCompletedNormally(f, r);
+                return r;
+            }};
+        assertEquals(21, (int) testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * get of a forked task returns when task completes
+     */
+    public void testForkGet() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() throws Exception {
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                Integer r = f.get();
+                assertEquals(21, (int) r);
+                checkCompletedNormally(f, r);
+                return r;
+            }};
+        assertEquals(21, (int) testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * timed get of a forked task returns when task completes
+     */
+    public void testForkTimedGet() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() throws Exception {
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                Integer r = f.get(5L, SECONDS);
+                assertEquals(21, (int) r);
+                checkCompletedNormally(f, r);
+                return r;
+            }};
+        assertEquals(21, (int) testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes
+     */
+    public void testForkQuietlyJoin() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                Integer r = f.getRawResult();
+                assertEquals(21, (int) r);
+                checkCompletedNormally(f, r);
+                return r;
+            }};
+        assertEquals(21, (int) testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * helpQuiesce returns when tasks are complete.
+     * getQueuedTaskCount returns 0 when quiescent
+     */
+    public void testForkHelpQuiesce() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                helpQuiesce();
+                while (!f.isDone()) // wait out race
+                    ;
+                assertEquals(0, getQueuedTaskCount());
+                checkCompletedNormally(f, 21);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invoke task throws exception when task completes abnormally
+     */
+    public void testAbnormalInvoke() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FailingFibTask f = new FailingFibTask(8);
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes abnormally
+     */
+    public void testAbnormalQuietlyInvoke() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FailingFibTask f = new FailingFibTask(8);
+                f.quietlyInvoke();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * join of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkJoin() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FailingFibTask f = new FailingFibTask(8);
+                assertSame(f, f.fork());
+                try {
+                    Integer r = f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkGet() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() throws Exception {
+                FailingFibTask f = new FailingFibTask(8);
+                assertSame(f, f.fork());
+                try {
+                    Integer r = f.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * timed get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkTimedGet() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() throws Exception {
+                FailingFibTask f = new FailingFibTask(8);
+                assertSame(f, f.fork());
+                try {
+                    Integer r = f.get(5L, SECONDS);
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes abnormally
+     */
+    public void testAbnormalForkQuietlyJoin() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FailingFibTask f = new FailingFibTask(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invoke task throws exception when task cancelled
+     */
+    public void testCancelledInvoke() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                assertTrue(f.cancel(true));
+                try {
+                    Integer r = f.invoke();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * join of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkJoin() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    Integer r = f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkGet() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() throws Exception {
+                FibTask f = new FibTask(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    Integer r = f.get();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * timed get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkTimedGet() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() throws Exception {
+                FibTask f = new FibTask(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    Integer r = f.get(5L, SECONDS);
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task cancelled
+     */
+    public void testCancelledForkQuietlyJoin() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                checkCancelled(f);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * getPool of executing task returns its pool
+     */
+    public void testGetPool() {
+        final ForkJoinPool mainPool = mainPool();
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                assertSame(mainPool, getPool());
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool, a));
+    }
+
+    /**
+     * getPool of non-FJ task returns null
+     */
+    public void testGetPool2() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                assertNull(getPool());
+                return NoResult;
+            }};
+        assertSame(NoResult, a.invoke());
+    }
+
+    /**
+     * inForkJoinPool of executing task returns true
+     */
+    public void testInForkJoinPool() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                assertTrue(inForkJoinPool());
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * inForkJoinPool of non-FJ task returns false
+     */
+    public void testInForkJoinPool2() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                assertFalse(inForkJoinPool());
+                return NoResult;
+            }};
+        assertSame(NoResult, a.invoke());
+    }
+
+    /**
+     * The value set by setRawResult is returned by getRawResult
+     */
+    public void testSetRawResult() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                setRawResult(NoResult);
+                assertSame(NoResult, getRawResult());
+                return NoResult;
+            }
+        };
+        assertSame(NoResult, a.invoke());
+    }
+
+    /**
+     * A reinitialized normally completed task may be re-invoked
+     */
+    public void testReinitialize() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                checkNotDone(f);
+
+                for (int i = 0; i < 3; i++) {
+                    Integer r = f.invoke();
+                    assertEquals(21, (int) r);
+                    checkCompletedNormally(f, r);
+                    f.reinitialize();
+                    f.publicSetRawResult(null);
+                    checkNotDone(f);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * A reinitialized abnormally completed task may be re-invoked
+     */
+    public void testReinitializeAbnormal() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FailingFibTask f = new FailingFibTask(8);
+                checkNotDone(f);
+
+                for (int i = 0; i < 3; i++) {
+                    try {
+                        f.invoke();
+                        shouldThrow();
+                    } catch (FJException success) {
+                        checkCompletedAbnormally(f, success);
+                    }
+                    f.reinitialize();
+                    checkNotDone(f);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invoke task throws exception after invoking completeExceptionally
+     */
+    public void testCompleteExceptionally() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                f.completeExceptionally(new FJException());
+                try {
+                    Integer r = f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invoke task suppresses execution invoking complete
+     */
+    public void testComplete() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                f.complete(NoResult);
+                Integer r = f.invoke();
+                assertSame(NoResult, r);
+                checkCompletedNormally(f, NoResult);
+                return r;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invokeAll(t1, t2) invokes all task arguments
+     */
+    public void testInvokeAll2() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                FibTask g = new FibTask(9);
+                invokeAll(f, g);
+                checkCompletedNormally(f, 21);
+                checkCompletedNormally(g, 34);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument invokes task
+     */
+    public void testInvokeAll1() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                invokeAll(f);
+                checkCompletedNormally(f, 21);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument invokes tasks
+     */
+    public void testInvokeAll3() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                FibTask g = new FibTask(9);
+                FibTask h = new FibTask(7);
+                invokeAll(f, g, h);
+                assertTrue(f.isDone());
+                assertTrue(g.isDone());
+                assertTrue(h.isDone());
+                checkCompletedNormally(f, 21);
+                checkCompletedNormally(g, 34);
+                checkCompletedNormally(h, 13);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invokeAll(collection) invokes all tasks in the collection
+     */
+    public void testInvokeAllCollection() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                FibTask g = new FibTask(9);
+                FibTask h = new FibTask(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                invokeAll(set);
+                assertTrue(f.isDone());
+                assertTrue(g.isDone());
+                assertTrue(h.isDone());
+                checkCompletedNormally(f, 21);
+                checkCompletedNormally(g, 34);
+                checkCompletedNormally(h, 13);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invokeAll(tasks) with any null task throws NPE
+     */
+    public void testInvokeAllNPE() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                FibTask g = new FibTask(9);
+                FibTask h = null;
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invokeAll(t1, t2) throw exception if any task does
+     */
+    public void testAbnormalInvokeAll2() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                FailingFibTask g = new FailingFibTask(9);
+                try {
+                    invokeAll(f, g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument throws exception if task does
+     */
+    public void testAbnormalInvokeAll1() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FailingFibTask g = new FailingFibTask(9);
+                try {
+                    invokeAll(g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument throws exception if any task does
+     */
+    public void testAbnormalInvokeAll3() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                FailingFibTask g = new FailingFibTask(9);
+                FibTask h = new FibTask(7);
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invokeAll(collection) throws exception if any task does
+     */
+    public void testAbnormalInvokeAllCollection() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FailingFibTask f = new FailingFibTask(8);
+                FibTask g = new FibTask(9);
+                FibTask h = new FibTask(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                try {
+                    invokeAll(set);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * tryUnfork returns true for most recent unexecuted task,
+     * and suppresses execution
+     */
+    public void testTryUnfork() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask g = new FibTask(9);
+                assertSame(g, g.fork());
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                assertTrue(f.tryUnfork());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g, 34);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(singletonPool(), a));
+    }
+
+    /**
+     * getSurplusQueuedTaskCount returns > 0 when
+     * there are more tasks than threads
+     */
+    public void testGetSurplusQueuedTaskCount() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask h = new FibTask(7);
+                assertSame(h, h.fork());
+                FibTask g = new FibTask(9);
+                assertSame(g, g.fork());
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                assertTrue(getSurplusQueuedTaskCount() > 0);
+                helpQuiesce();
+                assertEquals(0, getSurplusQueuedTaskCount());
+                checkCompletedNormally(f, 21);
+                checkCompletedNormally(g, 34);
+                checkCompletedNormally(h, 13);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(singletonPool(), a));
+    }
+
+    /**
+     * peekNextLocalTask returns most recent unexecuted task.
+     */
+    public void testPeekNextLocalTask() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask g = new FibTask(9);
+                assertSame(g, g.fork());
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                assertSame(f, peekNextLocalTask());
+                checkCompletesNormally(f, 21);
+                helpQuiesce();
+                checkCompletedNormally(g, 34);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(singletonPool(), a));
+    }
+
+    /**
+     * pollNextLocalTask returns most recent unexecuted task
+     * without executing it
+     */
+    public void testPollNextLocalTask() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask g = new FibTask(9);
+                assertSame(g, g.fork());
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                assertSame(f, pollNextLocalTask());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g, 34);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(singletonPool(), a));
+    }
+
+    /**
+     * pollTask returns an unexecuted task without executing it
+     */
+    public void testPollTask() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask g = new FibTask(9);
+                assertSame(g, g.fork());
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                assertSame(f, pollTask());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g, 34);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(singletonPool(), a));
+    }
+
+    /**
+     * peekNextLocalTask returns least recent unexecuted task in async mode
+     */
+    public void testPeekNextLocalTaskAsync() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask g = new FibTask(9);
+                assertSame(g, g.fork());
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                assertSame(g, peekNextLocalTask());
+                assertEquals(21, (int) f.join());
+                helpQuiesce();
+                checkCompletedNormally(f, 21);
+                checkCompletedNormally(g, 34);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(asyncSingletonPool(), a));
+    }
+
+    /**
+     * pollNextLocalTask returns least recent unexecuted task without
+     * executing it, in async mode
+     */
+    public void testPollNextLocalTaskAsync() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask g = new FibTask(9);
+                assertSame(g, g.fork());
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                assertSame(g, pollNextLocalTask());
+                helpQuiesce();
+                checkCompletedNormally(f, 21);
+                checkNotDone(g);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(asyncSingletonPool(), a));
+    }
+
+    /**
+     * pollTask returns an unexecuted task without executing it, in
+     * async mode
+     */
+    public void testPollTaskAsync() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask g = new FibTask(9);
+                assertSame(g, g.fork());
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                assertSame(g, pollTask());
+                helpQuiesce();
+                checkCompletedNormally(f, 21);
+                checkNotDone(g);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(asyncSingletonPool(), a));
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/ReentrantLockTest.java b/jdk/test/java/util/concurrent/tck/ReentrantLockTest.java
new file mode 100644
index 00000000000..8088498562a
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/ReentrantLockTest.java
@@ -0,0 +1,1163 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ReentrantLockTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ReentrantLockTest.class);
+    }
+
+    /**
+     * A checked runnable calling lockInterruptibly
+     */
+    class InterruptibleLockRunnable extends CheckedRunnable {
+        final ReentrantLock lock;
+        InterruptibleLockRunnable(ReentrantLock lock) { this.lock = lock; }
+        public void realRun() throws InterruptedException {
+            lock.lockInterruptibly();
+        }
+    }
+
+    /**
+     * A checked runnable calling lockInterruptibly that expects to be
+     * interrupted
+     */
+    class InterruptedLockRunnable extends CheckedInterruptedRunnable {
+        final ReentrantLock lock;
+        InterruptedLockRunnable(ReentrantLock lock) { this.lock = lock; }
+        public void realRun() throws InterruptedException {
+            lock.lockInterruptibly();
+        }
+    }
+
+    /**
+     * Subclass to expose protected methods
+     */
+    static class PublicReentrantLock extends ReentrantLock {
+        PublicReentrantLock() { super(); }
+        PublicReentrantLock(boolean fair) { super(fair); }
+        public Thread getOwner() {
+            return super.getOwner();
+        }
+        public Collection<Thread> getQueuedThreads() {
+            return super.getQueuedThreads();
+        }
+        public Collection<Thread> getWaitingThreads(Condition c) {
+            return super.getWaitingThreads(c);
+        }
+    }
+
+    /**
+     * Releases write lock, checking that it had a hold count of 1.
+     */
+    void releaseLock(PublicReentrantLock lock) {
+        assertLockedByMoi(lock);
+        lock.unlock();
+        assertFalse(lock.isHeldByCurrentThread());
+        assertNotLocked(lock);
+    }
+
+    /**
+     * Spin-waits until lock.hasQueuedThread(t) becomes true.
+     */
+    void waitForQueuedThread(PublicReentrantLock lock, Thread t) {
+        long startTime = System.nanoTime();
+        while (!lock.hasQueuedThread(t)) {
+            if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                throw new AssertionFailedError("timed out");
+            Thread.yield();
+        }
+        assertTrue(t.isAlive());
+        assertNotSame(t, lock.getOwner());
+    }
+
+    /**
+     * Checks that lock is not locked.
+     */
+    void assertNotLocked(PublicReentrantLock lock) {
+        assertFalse(lock.isLocked());
+        assertFalse(lock.isHeldByCurrentThread());
+        assertNull(lock.getOwner());
+        assertEquals(0, lock.getHoldCount());
+    }
+
+    /**
+     * Checks that lock is locked by the given thread.
+     */
+    void assertLockedBy(PublicReentrantLock lock, Thread t) {
+        assertTrue(lock.isLocked());
+        assertSame(t, lock.getOwner());
+        assertEquals(t == Thread.currentThread(),
+                     lock.isHeldByCurrentThread());
+        assertEquals(t == Thread.currentThread(),
+                     lock.getHoldCount() > 0);
+    }
+
+    /**
+     * Checks that lock is locked by the current thread.
+     */
+    void assertLockedByMoi(PublicReentrantLock lock) {
+        assertLockedBy(lock, Thread.currentThread());
+    }
+
+    /**
+     * Checks that condition c has no waiters.
+     */
+    void assertHasNoWaiters(PublicReentrantLock lock, Condition c) {
+        assertHasWaiters(lock, c, new Thread[] {});
+    }
+
+    /**
+     * Checks that condition c has exactly the given waiter threads.
+     */
+    void assertHasWaiters(PublicReentrantLock lock, Condition c,
+                          Thread... threads) {
+        lock.lock();
+        assertEquals(threads.length > 0, lock.hasWaiters(c));
+        assertEquals(threads.length, lock.getWaitQueueLength(c));
+        assertEquals(threads.length == 0, lock.getWaitingThreads(c).isEmpty());
+        assertEquals(threads.length, lock.getWaitingThreads(c).size());
+        assertEquals(new HashSet<Thread>(lock.getWaitingThreads(c)),
+                     new HashSet<Thread>(Arrays.asList(threads)));
+        lock.unlock();
+    }
+
+    enum AwaitMethod { await, awaitTimed, awaitNanos, awaitUntil }
+
+    /**
+     * Awaits condition "indefinitely" using the specified AwaitMethod.
+     */
+    void await(Condition c, AwaitMethod awaitMethod)
+            throws InterruptedException {
+        long timeoutMillis = 2 * LONG_DELAY_MS;
+        switch (awaitMethod) {
+        case await:
+            c.await();
+            break;
+        case awaitTimed:
+            assertTrue(c.await(timeoutMillis, MILLISECONDS));
+            break;
+        case awaitNanos:
+            long timeoutNanos = MILLISECONDS.toNanos(timeoutMillis);
+            long nanosRemaining = c.awaitNanos(timeoutNanos);
+            assertTrue(nanosRemaining > timeoutNanos / 2);
+            assertTrue(nanosRemaining <= timeoutNanos);
+            break;
+        case awaitUntil:
+            assertTrue(c.awaitUntil(delayedDate(timeoutMillis)));
+            break;
+        default:
+            throw new AssertionError();
+        }
+    }
+
+    /**
+     * Constructor sets given fairness, and is in unlocked state
+     */
+    public void testConstructor() {
+        PublicReentrantLock lock;
+
+        lock = new PublicReentrantLock();
+        assertFalse(lock.isFair());
+        assertNotLocked(lock);
+
+        lock = new PublicReentrantLock(true);
+        assertTrue(lock.isFair());
+        assertNotLocked(lock);
+
+        lock = new PublicReentrantLock(false);
+        assertFalse(lock.isFair());
+        assertNotLocked(lock);
+    }
+
+    /**
+     * locking an unlocked lock succeeds
+     */
+    public void testLock()      { testLock(false); }
+    public void testLock_fair() { testLock(true); }
+    public void testLock(boolean fair) {
+        PublicReentrantLock lock = new PublicReentrantLock(fair);
+        lock.lock();
+        assertLockedByMoi(lock);
+        releaseLock(lock);
+    }
+
+    /**
+     * Unlocking an unlocked lock throws IllegalMonitorStateException
+     */
+    public void testUnlock_IMSE()      { testUnlock_IMSE(false); }
+    public void testUnlock_IMSE_fair() { testUnlock_IMSE(true); }
+    public void testUnlock_IMSE(boolean fair) {
+        ReentrantLock lock = new ReentrantLock(fair);
+        try {
+            lock.unlock();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * tryLock on an unlocked lock succeeds
+     */
+    public void testTryLock()      { testTryLock(false); }
+    public void testTryLock_fair() { testTryLock(true); }
+    public void testTryLock(boolean fair) {
+        PublicReentrantLock lock = new PublicReentrantLock(fair);
+        assertTrue(lock.tryLock());
+        assertLockedByMoi(lock);
+        assertTrue(lock.tryLock());
+        assertLockedByMoi(lock);
+        lock.unlock();
+        releaseLock(lock);
+    }
+
+    /**
+     * hasQueuedThreads reports whether there are waiting threads
+     */
+    public void testHasQueuedThreads()      { testHasQueuedThreads(false); }
+    public void testHasQueuedThreads_fair() { testHasQueuedThreads(true); }
+    public void testHasQueuedThreads(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        Thread t1 = new Thread(new InterruptedLockRunnable(lock));
+        Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
+        assertFalse(lock.hasQueuedThreads());
+        lock.lock();
+        assertFalse(lock.hasQueuedThreads());
+        t1.start();
+        waitForQueuedThread(lock, t1);
+        assertTrue(lock.hasQueuedThreads());
+        t2.start();
+        waitForQueuedThread(lock, t2);
+        assertTrue(lock.hasQueuedThreads());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertTrue(lock.hasQueuedThreads());
+        lock.unlock();
+        awaitTermination(t2);
+        assertFalse(lock.hasQueuedThreads());
+    }
+
+    /**
+     * getQueueLength reports number of waiting threads
+     */
+    public void testGetQueueLength()      { testGetQueueLength(false); }
+    public void testGetQueueLength_fair() { testGetQueueLength(true); }
+    public void testGetQueueLength(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        Thread t1 = new Thread(new InterruptedLockRunnable(lock));
+        Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
+        assertEquals(0, lock.getQueueLength());
+        lock.lock();
+        t1.start();
+        waitForQueuedThread(lock, t1);
+        assertEquals(1, lock.getQueueLength());
+        t2.start();
+        waitForQueuedThread(lock, t2);
+        assertEquals(2, lock.getQueueLength());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertEquals(1, lock.getQueueLength());
+        lock.unlock();
+        awaitTermination(t2);
+        assertEquals(0, lock.getQueueLength());
+    }
+
+    /**
+     * hasQueuedThread(null) throws NPE
+     */
+    public void testHasQueuedThreadNPE()      { testHasQueuedThreadNPE(false); }
+    public void testHasQueuedThreadNPE_fair() { testHasQueuedThreadNPE(true); }
+    public void testHasQueuedThreadNPE(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        try {
+            lock.hasQueuedThread(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * hasQueuedThread reports whether a thread is queued
+     */
+    public void testHasQueuedThread()      { testHasQueuedThread(false); }
+    public void testHasQueuedThread_fair() { testHasQueuedThread(true); }
+    public void testHasQueuedThread(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        Thread t1 = new Thread(new InterruptedLockRunnable(lock));
+        Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
+        assertFalse(lock.hasQueuedThread(t1));
+        assertFalse(lock.hasQueuedThread(t2));
+        lock.lock();
+        t1.start();
+        waitForQueuedThread(lock, t1);
+        assertTrue(lock.hasQueuedThread(t1));
+        assertFalse(lock.hasQueuedThread(t2));
+        t2.start();
+        waitForQueuedThread(lock, t2);
+        assertTrue(lock.hasQueuedThread(t1));
+        assertTrue(lock.hasQueuedThread(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertFalse(lock.hasQueuedThread(t1));
+        assertTrue(lock.hasQueuedThread(t2));
+        lock.unlock();
+        awaitTermination(t2);
+        assertFalse(lock.hasQueuedThread(t1));
+        assertFalse(lock.hasQueuedThread(t2));
+    }
+
+    /**
+     * getQueuedThreads includes waiting threads
+     */
+    public void testGetQueuedThreads()      { testGetQueuedThreads(false); }
+    public void testGetQueuedThreads_fair() { testGetQueuedThreads(true); }
+    public void testGetQueuedThreads(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        Thread t1 = new Thread(new InterruptedLockRunnable(lock));
+        Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
+        assertTrue(lock.getQueuedThreads().isEmpty());
+        lock.lock();
+        assertTrue(lock.getQueuedThreads().isEmpty());
+        t1.start();
+        waitForQueuedThread(lock, t1);
+        assertEquals(1, lock.getQueuedThreads().size());
+        assertTrue(lock.getQueuedThreads().contains(t1));
+        t2.start();
+        waitForQueuedThread(lock, t2);
+        assertEquals(2, lock.getQueuedThreads().size());
+        assertTrue(lock.getQueuedThreads().contains(t1));
+        assertTrue(lock.getQueuedThreads().contains(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertFalse(lock.getQueuedThreads().contains(t1));
+        assertTrue(lock.getQueuedThreads().contains(t2));
+        assertEquals(1, lock.getQueuedThreads().size());
+        lock.unlock();
+        awaitTermination(t2);
+        assertTrue(lock.getQueuedThreads().isEmpty());
+    }
+
+    /**
+     * timed tryLock is interruptible
+     */
+    public void testTryLock_Interruptible()      { testTryLock_Interruptible(false); }
+    public void testTryLock_Interruptible_fair() { testTryLock_Interruptible(true); }
+    public void testTryLock_Interruptible(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        lock.lock();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.tryLock(2 * LONG_DELAY_MS, MILLISECONDS);
+            }});
+
+        waitForQueuedThread(lock, t);
+        t.interrupt();
+        awaitTermination(t);
+        releaseLock(lock);
+    }
+
+    /**
+     * tryLock on a locked lock fails
+     */
+    public void testTryLockWhenLocked()      { testTryLockWhenLocked(false); }
+    public void testTryLockWhenLocked_fair() { testTryLockWhenLocked(true); }
+    public void testTryLockWhenLocked(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        lock.lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertFalse(lock.tryLock());
+            }});
+
+        awaitTermination(t);
+        releaseLock(lock);
+    }
+
+    /**
+     * Timed tryLock on a locked lock times out
+     */
+    public void testTryLock_Timeout()      { testTryLock_Timeout(false); }
+    public void testTryLock_Timeout_fair() { testTryLock_Timeout(true); }
+    public void testTryLock_Timeout(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        lock.lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                long timeoutMillis = 10;
+                assertFalse(lock.tryLock(timeoutMillis, MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+            }});
+
+        awaitTermination(t);
+        releaseLock(lock);
+    }
+
+    /**
+     * getHoldCount returns number of recursive holds
+     */
+    public void testGetHoldCount()      { testGetHoldCount(false); }
+    public void testGetHoldCount_fair() { testGetHoldCount(true); }
+    public void testGetHoldCount(boolean fair) {
+        ReentrantLock lock = new ReentrantLock(fair);
+        for (int i = 1; i <= SIZE; i++) {
+            lock.lock();
+            assertEquals(i, lock.getHoldCount());
+        }
+        for (int i = SIZE; i > 0; i--) {
+            lock.unlock();
+            assertEquals(i - 1, lock.getHoldCount());
+        }
+    }
+
+    /**
+     * isLocked is true when locked and false when not
+     */
+    public void testIsLocked()      { testIsLocked(false); }
+    public void testIsLocked_fair() { testIsLocked(true); }
+    public void testIsLocked(boolean fair) {
+        try {
+            final ReentrantLock lock = new ReentrantLock(fair);
+            assertFalse(lock.isLocked());
+            lock.lock();
+            assertTrue(lock.isLocked());
+            lock.lock();
+            assertTrue(lock.isLocked());
+            lock.unlock();
+            assertTrue(lock.isLocked());
+            lock.unlock();
+            assertFalse(lock.isLocked());
+            final CyclicBarrier barrier = new CyclicBarrier(2);
+            Thread t = newStartedThread(new CheckedRunnable() {
+                    public void realRun() throws Exception {
+                        lock.lock();
+                        assertTrue(lock.isLocked());
+                        barrier.await();
+                        barrier.await();
+                        lock.unlock();
+                    }});
+
+            barrier.await();
+            assertTrue(lock.isLocked());
+            barrier.await();
+            awaitTermination(t);
+            assertFalse(lock.isLocked());
+        } catch (Exception fail) { threadUnexpectedException(fail); }
+    }
+
+    /**
+     * lockInterruptibly succeeds when unlocked, else is interruptible
+     */
+    public void testLockInterruptibly()      { testLockInterruptibly(false); }
+    public void testLockInterruptibly_fair() { testLockInterruptibly(true); }
+    public void testLockInterruptibly(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        try {
+            lock.lockInterruptibly();
+        } catch (InterruptedException fail) { threadUnexpectedException(fail); }
+        assertLockedByMoi(lock);
+        Thread t = newStartedThread(new InterruptedLockRunnable(lock));
+        waitForQueuedThread(lock, t);
+        t.interrupt();
+        assertTrue(lock.isLocked());
+        assertTrue(lock.isHeldByCurrentThread());
+        awaitTermination(t);
+        releaseLock(lock);
+    }
+
+    /**
+     * Calling await without holding lock throws IllegalMonitorStateException
+     */
+    public void testAwait_IMSE()      { testAwait_IMSE(false); }
+    public void testAwait_IMSE_fair() { testAwait_IMSE(true); }
+    public void testAwait_IMSE(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        for (AwaitMethod awaitMethod : AwaitMethod.values()) {
+            long startTime = System.nanoTime();
+            try {
+                await(c, awaitMethod);
+                shouldThrow();
+            } catch (IllegalMonitorStateException success) {
+            } catch (InterruptedException e) { threadUnexpectedException(e); }
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * Calling signal without holding lock throws IllegalMonitorStateException
+     */
+    public void testSignal_IMSE()      { testSignal_IMSE(false); }
+    public void testSignal_IMSE_fair() { testSignal_IMSE(true); }
+    public void testSignal_IMSE(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        try {
+            c.signal();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * awaitNanos without a signal times out
+     */
+    public void testAwaitNanos_Timeout()      { testAwaitNanos_Timeout(false); }
+    public void testAwaitNanos_Timeout_fair() { testAwaitNanos_Timeout(true); }
+    public void testAwaitNanos_Timeout(boolean fair) {
+        try {
+            final ReentrantLock lock = new ReentrantLock(fair);
+            final Condition c = lock.newCondition();
+            lock.lock();
+            long startTime = System.nanoTime();
+            long timeoutMillis = 10;
+            long timeoutNanos = MILLISECONDS.toNanos(timeoutMillis);
+            long nanosRemaining = c.awaitNanos(timeoutNanos);
+            assertTrue(nanosRemaining <= 0);
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+            lock.unlock();
+        } catch (InterruptedException fail) { threadUnexpectedException(fail); }
+    }
+
+    /**
+     * timed await without a signal times out
+     */
+    public void testAwait_Timeout()      { testAwait_Timeout(false); }
+    public void testAwait_Timeout_fair() { testAwait_Timeout(true); }
+    public void testAwait_Timeout(boolean fair) {
+        try {
+            final ReentrantLock lock = new ReentrantLock(fair);
+            final Condition c = lock.newCondition();
+            lock.lock();
+            long startTime = System.nanoTime();
+            long timeoutMillis = 10;
+            assertFalse(c.await(timeoutMillis, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+            lock.unlock();
+        } catch (InterruptedException fail) { threadUnexpectedException(fail); }
+    }
+
+    /**
+     * awaitUntil without a signal times out
+     */
+    public void testAwaitUntil_Timeout()      { testAwaitUntil_Timeout(false); }
+    public void testAwaitUntil_Timeout_fair() { testAwaitUntil_Timeout(true); }
+    public void testAwaitUntil_Timeout(boolean fair) {
+        try {
+            final ReentrantLock lock = new ReentrantLock(fair);
+            final Condition c = lock.newCondition();
+            lock.lock();
+            // We shouldn't assume that nanoTime and currentTimeMillis
+            // use the same time source, so don't use nanoTime here.
+            java.util.Date delayedDate = delayedDate(timeoutMillis());
+            assertFalse(c.awaitUntil(delayedDate));
+            assertTrue(new java.util.Date().getTime() >= delayedDate.getTime());
+            lock.unlock();
+        } catch (InterruptedException fail) { threadUnexpectedException(fail); }
+    }
+
+    /**
+     * await returns when signalled
+     */
+    public void testAwait()      { testAwait(false); }
+    public void testAwait_fair() { testAwait(true); }
+    public void testAwait(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final CountDownLatch locked = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                locked.countDown();
+                c.await();
+                lock.unlock();
+            }});
+
+        await(locked);
+        lock.lock();
+        assertHasWaiters(lock, c, t);
+        c.signal();
+        assertHasNoWaiters(lock, c);
+        assertTrue(t.isAlive());
+        lock.unlock();
+        awaitTermination(t);
+    }
+
+    /**
+     * hasWaiters throws NPE if null
+     */
+    public void testHasWaitersNPE()      { testHasWaitersNPE(false); }
+    public void testHasWaitersNPE_fair() { testHasWaitersNPE(true); }
+    public void testHasWaitersNPE(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        try {
+            lock.hasWaiters(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * getWaitQueueLength throws NPE if null
+     */
+    public void testGetWaitQueueLengthNPE()      { testGetWaitQueueLengthNPE(false); }
+    public void testGetWaitQueueLengthNPE_fair() { testGetWaitQueueLengthNPE(true); }
+    public void testGetWaitQueueLengthNPE(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        try {
+            lock.getWaitQueueLength(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * getWaitingThreads throws NPE if null
+     */
+    public void testGetWaitingThreadsNPE()      { testGetWaitingThreadsNPE(false); }
+    public void testGetWaitingThreadsNPE_fair() { testGetWaitingThreadsNPE(true); }
+    public void testGetWaitingThreadsNPE(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        try {
+            lock.getWaitingThreads(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * hasWaiters throws IllegalArgumentException if not owned
+     */
+    public void testHasWaitersIAE()      { testHasWaitersIAE(false); }
+    public void testHasWaitersIAE_fair() { testHasWaitersIAE(true); }
+    public void testHasWaitersIAE(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final ReentrantLock lock2 = new ReentrantLock(fair);
+        try {
+            lock2.hasWaiters(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * hasWaiters throws IllegalMonitorStateException if not locked
+     */
+    public void testHasWaitersIMSE()      { testHasWaitersIMSE(false); }
+    public void testHasWaitersIMSE_fair() { testHasWaitersIMSE(true); }
+    public void testHasWaitersIMSE(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        try {
+            lock.hasWaiters(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * getWaitQueueLength throws IllegalArgumentException if not owned
+     */
+    public void testGetWaitQueueLengthIAE()      { testGetWaitQueueLengthIAE(false); }
+    public void testGetWaitQueueLengthIAE_fair() { testGetWaitQueueLengthIAE(true); }
+    public void testGetWaitQueueLengthIAE(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final ReentrantLock lock2 = new ReentrantLock(fair);
+        try {
+            lock2.getWaitQueueLength(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * getWaitQueueLength throws IllegalMonitorStateException if not locked
+     */
+    public void testGetWaitQueueLengthIMSE()      { testGetWaitQueueLengthIMSE(false); }
+    public void testGetWaitQueueLengthIMSE_fair() { testGetWaitQueueLengthIMSE(true); }
+    public void testGetWaitQueueLengthIMSE(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        try {
+            lock.getWaitQueueLength(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * getWaitingThreads throws IllegalArgumentException if not owned
+     */
+    public void testGetWaitingThreadsIAE()      { testGetWaitingThreadsIAE(false); }
+    public void testGetWaitingThreadsIAE_fair() { testGetWaitingThreadsIAE(true); }
+    public void testGetWaitingThreadsIAE(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final PublicReentrantLock lock2 = new PublicReentrantLock(fair);
+        try {
+            lock2.getWaitingThreads(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * getWaitingThreads throws IllegalMonitorStateException if not locked
+     */
+    public void testGetWaitingThreadsIMSE()      { testGetWaitingThreadsIMSE(false); }
+    public void testGetWaitingThreadsIMSE_fair() { testGetWaitingThreadsIMSE(true); }
+    public void testGetWaitingThreadsIMSE(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        try {
+            lock.getWaitingThreads(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * hasWaiters returns true when a thread is waiting, else false
+     */
+    public void testHasWaiters()      { testHasWaiters(false); }
+    public void testHasWaiters_fair() { testHasWaiters(true); }
+    public void testHasWaiters(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final CountDownLatch pleaseSignal = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                assertHasNoWaiters(lock, c);
+                assertFalse(lock.hasWaiters(c));
+                pleaseSignal.countDown();
+                c.await();
+                assertHasNoWaiters(lock, c);
+                assertFalse(lock.hasWaiters(c));
+                lock.unlock();
+            }});
+
+        await(pleaseSignal);
+        lock.lock();
+        assertHasWaiters(lock, c, t);
+        assertTrue(lock.hasWaiters(c));
+        c.signal();
+        assertHasNoWaiters(lock, c);
+        assertFalse(lock.hasWaiters(c));
+        lock.unlock();
+        awaitTermination(t);
+        assertHasNoWaiters(lock, c);
+    }
+
+    /**
+     * getWaitQueueLength returns number of waiting threads
+     */
+    public void testGetWaitQueueLength()      { testGetWaitQueueLength(false); }
+    public void testGetWaitQueueLength_fair() { testGetWaitQueueLength(true); }
+    public void testGetWaitQueueLength(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final CountDownLatch locked1 = new CountDownLatch(1);
+        final CountDownLatch locked2 = new CountDownLatch(1);
+        Thread t1 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                assertFalse(lock.hasWaiters(c));
+                assertEquals(0, lock.getWaitQueueLength(c));
+                locked1.countDown();
+                c.await();
+                lock.unlock();
+            }});
+
+        Thread t2 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                assertTrue(lock.hasWaiters(c));
+                assertEquals(1, lock.getWaitQueueLength(c));
+                locked2.countDown();
+                c.await();
+                lock.unlock();
+            }});
+
+        lock.lock();
+        assertEquals(0, lock.getWaitQueueLength(c));
+        lock.unlock();
+
+        t1.start();
+        await(locked1);
+
+        lock.lock();
+        assertHasWaiters(lock, c, t1);
+        assertEquals(1, lock.getWaitQueueLength(c));
+        lock.unlock();
+
+        t2.start();
+        await(locked2);
+
+        lock.lock();
+        assertHasWaiters(lock, c, t1, t2);
+        assertEquals(2, lock.getWaitQueueLength(c));
+        c.signalAll();
+        assertHasNoWaiters(lock, c);
+        lock.unlock();
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+
+        assertHasNoWaiters(lock, c);
+    }
+
+    /**
+     * getWaitingThreads returns only and all waiting threads
+     */
+    public void testGetWaitingThreads()      { testGetWaitingThreads(false); }
+    public void testGetWaitingThreads_fair() { testGetWaitingThreads(true); }
+    public void testGetWaitingThreads(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final CountDownLatch locked1 = new CountDownLatch(1);
+        final CountDownLatch locked2 = new CountDownLatch(1);
+        Thread t1 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                assertTrue(lock.getWaitingThreads(c).isEmpty());
+                locked1.countDown();
+                c.await();
+                lock.unlock();
+            }});
+
+        Thread t2 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                assertFalse(lock.getWaitingThreads(c).isEmpty());
+                locked2.countDown();
+                c.await();
+                lock.unlock();
+            }});
+
+        lock.lock();
+        assertTrue(lock.getWaitingThreads(c).isEmpty());
+        lock.unlock();
+
+        t1.start();
+        await(locked1);
+
+        lock.lock();
+        assertHasWaiters(lock, c, t1);
+        assertTrue(lock.getWaitingThreads(c).contains(t1));
+        assertFalse(lock.getWaitingThreads(c).contains(t2));
+        assertEquals(1, lock.getWaitingThreads(c).size());
+        lock.unlock();
+
+        t2.start();
+        await(locked2);
+
+        lock.lock();
+        assertHasWaiters(lock, c, t1, t2);
+        assertTrue(lock.getWaitingThreads(c).contains(t1));
+        assertTrue(lock.getWaitingThreads(c).contains(t2));
+        assertEquals(2, lock.getWaitingThreads(c).size());
+        c.signalAll();
+        assertHasNoWaiters(lock, c);
+        lock.unlock();
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+
+        assertHasNoWaiters(lock, c);
+    }
+
+    /**
+     * awaitUninterruptibly is uninterruptible
+     */
+    public void testAwaitUninterruptibly()      { testAwaitUninterruptibly(false); }
+    public void testAwaitUninterruptibly_fair() { testAwaitUninterruptibly(true); }
+    public void testAwaitUninterruptibly(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(2);
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                // Interrupt before awaitUninterruptibly
+                lock.lock();
+                pleaseInterrupt.countDown();
+                Thread.currentThread().interrupt();
+                c.awaitUninterruptibly();
+                assertTrue(Thread.interrupted());
+                lock.unlock();
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                // Interrupt during awaitUninterruptibly
+                lock.lock();
+                pleaseInterrupt.countDown();
+                c.awaitUninterruptibly();
+                assertTrue(Thread.interrupted());
+                lock.unlock();
+            }});
+
+        await(pleaseInterrupt);
+        lock.lock();
+        lock.unlock();
+        t2.interrupt();
+
+        assertThreadStaysAlive(t1);
+        assertTrue(t2.isAlive());
+
+        lock.lock();
+        c.signalAll();
+        lock.unlock();
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * await/awaitNanos/awaitUntil is interruptible
+     */
+    public void testInterruptible_await()           { testInterruptible(false, AwaitMethod.await); }
+    public void testInterruptible_await_fair()      { testInterruptible(true,  AwaitMethod.await); }
+    public void testInterruptible_awaitTimed()      { testInterruptible(false, AwaitMethod.awaitTimed); }
+    public void testInterruptible_awaitTimed_fair() { testInterruptible(true,  AwaitMethod.awaitTimed); }
+    public void testInterruptible_awaitNanos()      { testInterruptible(false, AwaitMethod.awaitNanos); }
+    public void testInterruptible_awaitNanos_fair() { testInterruptible(true,  AwaitMethod.awaitNanos); }
+    public void testInterruptible_awaitUntil()      { testInterruptible(false, AwaitMethod.awaitUntil); }
+    public void testInterruptible_awaitUntil_fair() { testInterruptible(true,  AwaitMethod.awaitUntil); }
+    public void testInterruptible(boolean fair, final AwaitMethod awaitMethod) {
+        final PublicReentrantLock lock =
+            new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                assertLockedByMoi(lock);
+                assertHasNoWaiters(lock, c);
+                pleaseInterrupt.countDown();
+                try {
+                    await(c, awaitMethod);
+                } finally {
+                    assertLockedByMoi(lock);
+                    assertHasNoWaiters(lock, c);
+                    lock.unlock();
+                    assertFalse(Thread.interrupted());
+                }
+            }});
+
+        await(pleaseInterrupt);
+        assertHasWaiters(lock, c, t);
+        t.interrupt();
+        awaitTermination(t);
+        assertNotLocked(lock);
+    }
+
+    /**
+     * signalAll wakes up all threads
+     */
+    public void testSignalAll_await()           { testSignalAll(false, AwaitMethod.await); }
+    public void testSignalAll_await_fair()      { testSignalAll(true,  AwaitMethod.await); }
+    public void testSignalAll_awaitTimed()      { testSignalAll(false, AwaitMethod.awaitTimed); }
+    public void testSignalAll_awaitTimed_fair() { testSignalAll(true,  AwaitMethod.awaitTimed); }
+    public void testSignalAll_awaitNanos()      { testSignalAll(false, AwaitMethod.awaitNanos); }
+    public void testSignalAll_awaitNanos_fair() { testSignalAll(true,  AwaitMethod.awaitNanos); }
+    public void testSignalAll_awaitUntil()      { testSignalAll(false, AwaitMethod.awaitUntil); }
+    public void testSignalAll_awaitUntil_fair() { testSignalAll(true,  AwaitMethod.awaitUntil); }
+    public void testSignalAll(boolean fair, final AwaitMethod awaitMethod) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final CountDownLatch pleaseSignal = new CountDownLatch(2);
+        class Awaiter extends CheckedRunnable {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                pleaseSignal.countDown();
+                await(c, awaitMethod);
+                lock.unlock();
+            }
+        }
+
+        Thread t1 = newStartedThread(new Awaiter());
+        Thread t2 = newStartedThread(new Awaiter());
+
+        await(pleaseSignal);
+        lock.lock();
+        assertHasWaiters(lock, c, t1, t2);
+        c.signalAll();
+        assertHasNoWaiters(lock, c);
+        lock.unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * signal wakes up waiting threads in FIFO order
+     */
+    public void testSignalWakesFifo()      { testSignalWakesFifo(false); }
+    public void testSignalWakesFifo_fair() { testSignalWakesFifo(true); }
+    public void testSignalWakesFifo(boolean fair) {
+        final PublicReentrantLock lock =
+            new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final CountDownLatch locked1 = new CountDownLatch(1);
+        final CountDownLatch locked2 = new CountDownLatch(1);
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                locked1.countDown();
+                c.await();
+                lock.unlock();
+            }});
+
+        await(locked1);
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                locked2.countDown();
+                c.await();
+                lock.unlock();
+            }});
+
+        await(locked2);
+
+        lock.lock();
+        assertHasWaiters(lock, c, t1, t2);
+        assertFalse(lock.hasQueuedThreads());
+        c.signal();
+        assertHasWaiters(lock, c, t2);
+        assertTrue(lock.hasQueuedThread(t1));
+        assertFalse(lock.hasQueuedThread(t2));
+        c.signal();
+        assertHasNoWaiters(lock, c);
+        assertTrue(lock.hasQueuedThread(t1));
+        assertTrue(lock.hasQueuedThread(t2));
+        lock.unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * await after multiple reentrant locking preserves lock count
+     */
+    public void testAwaitLockCount()      { testAwaitLockCount(false); }
+    public void testAwaitLockCount_fair() { testAwaitLockCount(true); }
+    public void testAwaitLockCount(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final CountDownLatch pleaseSignal = new CountDownLatch(2);
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                assertLockedByMoi(lock);
+                assertEquals(1, lock.getHoldCount());
+                pleaseSignal.countDown();
+                c.await();
+                assertLockedByMoi(lock);
+                assertEquals(1, lock.getHoldCount());
+                lock.unlock();
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                lock.lock();
+                assertLockedByMoi(lock);
+                assertEquals(2, lock.getHoldCount());
+                pleaseSignal.countDown();
+                c.await();
+                assertLockedByMoi(lock);
+                assertEquals(2, lock.getHoldCount());
+                lock.unlock();
+                lock.unlock();
+            }});
+
+        await(pleaseSignal);
+        lock.lock();
+        assertHasWaiters(lock, c, t1, t2);
+        assertEquals(1, lock.getHoldCount());
+        c.signalAll();
+        assertHasNoWaiters(lock, c);
+        lock.unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * A serialized lock deserializes as unlocked
+     */
+    public void testSerialization()      { testSerialization(false); }
+    public void testSerialization_fair() { testSerialization(true); }
+    public void testSerialization(boolean fair) {
+        ReentrantLock lock = new ReentrantLock(fair);
+        lock.lock();
+
+        ReentrantLock clone = serialClone(lock);
+        assertEquals(lock.isFair(), clone.isFair());
+        assertTrue(lock.isLocked());
+        assertFalse(clone.isLocked());
+        assertEquals(1, lock.getHoldCount());
+        assertEquals(0, clone.getHoldCount());
+        clone.lock();
+        clone.lock();
+        assertTrue(clone.isLocked());
+        assertEquals(2, clone.getHoldCount());
+        assertEquals(1, lock.getHoldCount());
+        clone.unlock();
+        clone.unlock();
+        assertTrue(lock.isLocked());
+        assertFalse(clone.isLocked());
+    }
+
+    /**
+     * toString indicates current lock state
+     */
+    public void testToString()      { testToString(false); }
+    public void testToString_fair() { testToString(true); }
+    public void testToString(boolean fair) {
+        ReentrantLock lock = new ReentrantLock(fair);
+        assertTrue(lock.toString().contains("Unlocked"));
+        lock.lock();
+        assertTrue(lock.toString().contains("Locked"));
+        lock.unlock();
+        assertTrue(lock.toString().contains("Unlocked"));
+    }
+}
diff --git a/jdk/test/java/util/concurrent/tck/ReentrantReadWriteLockTest.java b/jdk/test/java/util/concurrent/tck/ReentrantReadWriteLockTest.java
new file mode 100644
index 00000000000..a2e597e5eb7
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/ReentrantReadWriteLockTest.java
@@ -0,0 +1,1703 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ReentrantReadWriteLockTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ReentrantReadWriteLockTest.class);
+    }
+
+    /**
+     * A runnable calling lockInterruptibly
+     */
+    class InterruptibleLockRunnable extends CheckedRunnable {
+        final ReentrantReadWriteLock lock;
+        InterruptibleLockRunnable(ReentrantReadWriteLock l) { lock = l; }
+        public void realRun() throws InterruptedException {
+            lock.writeLock().lockInterruptibly();
+        }
+    }
+
+    /**
+     * A runnable calling lockInterruptibly that expects to be
+     * interrupted
+     */
+    class InterruptedLockRunnable extends CheckedInterruptedRunnable {
+        final ReentrantReadWriteLock lock;
+        InterruptedLockRunnable(ReentrantReadWriteLock l) { lock = l; }
+        public void realRun() throws InterruptedException {
+            lock.writeLock().lockInterruptibly();
+        }
+    }
+
+    /**
+     * Subclass to expose protected methods
+     */
+    static class PublicReentrantReadWriteLock extends ReentrantReadWriteLock {
+        PublicReentrantReadWriteLock() { super(); }
+        PublicReentrantReadWriteLock(boolean fair) { super(fair); }
+        public Thread getOwner() {
+            return super.getOwner();
+        }
+        public Collection<Thread> getQueuedThreads() {
+            return super.getQueuedThreads();
+        }
+        public Collection<Thread> getWaitingThreads(Condition c) {
+            return super.getWaitingThreads(c);
+        }
+    }
+
+    /**
+     * Releases write lock, checking that it had a hold count of 1.
+     */
+    void releaseWriteLock(PublicReentrantReadWriteLock lock) {
+        ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
+        assertWriteLockedByMoi(lock);
+        assertEquals(1, lock.getWriteHoldCount());
+        writeLock.unlock();
+        assertNotWriteLocked(lock);
+    }
+
+    /**
+     * Spin-waits until lock.hasQueuedThread(t) becomes true.
+     */
+    void waitForQueuedThread(PublicReentrantReadWriteLock lock, Thread t) {
+        long startTime = System.nanoTime();
+        while (!lock.hasQueuedThread(t)) {
+            if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                throw new AssertionFailedError("timed out");
+            Thread.yield();
+        }
+        assertTrue(t.isAlive());
+        assertNotSame(t, lock.getOwner());
+    }
+
+    /**
+     * Checks that lock is not write-locked.
+     */
+    void assertNotWriteLocked(PublicReentrantReadWriteLock lock) {
+        assertFalse(lock.isWriteLocked());
+        assertFalse(lock.isWriteLockedByCurrentThread());
+        assertFalse(lock.writeLock().isHeldByCurrentThread());
+        assertEquals(0, lock.getWriteHoldCount());
+        assertEquals(0, lock.writeLock().getHoldCount());
+        assertNull(lock.getOwner());
+    }
+
+    /**
+     * Checks that lock is write-locked by the given thread.
+     */
+    void assertWriteLockedBy(PublicReentrantReadWriteLock lock, Thread t) {
+        assertTrue(lock.isWriteLocked());
+        assertSame(t, lock.getOwner());
+        assertEquals(t == Thread.currentThread(),
+                     lock.isWriteLockedByCurrentThread());
+        assertEquals(t == Thread.currentThread(),
+                     lock.writeLock().isHeldByCurrentThread());
+        assertEquals(t == Thread.currentThread(),
+                     lock.getWriteHoldCount() > 0);
+        assertEquals(t == Thread.currentThread(),
+                     lock.writeLock().getHoldCount() > 0);
+        assertEquals(0, lock.getReadLockCount());
+    }
+
+    /**
+     * Checks that lock is write-locked by the current thread.
+     */
+    void assertWriteLockedByMoi(PublicReentrantReadWriteLock lock) {
+        assertWriteLockedBy(lock, Thread.currentThread());
+    }
+
+    /**
+     * Checks that condition c has no waiters.
+     */
+    void assertHasNoWaiters(PublicReentrantReadWriteLock lock, Condition c) {
+        assertHasWaiters(lock, c, new Thread[] {});
+    }
+
+    /**
+     * Checks that condition c has exactly the given waiter threads.
+     */
+    void assertHasWaiters(PublicReentrantReadWriteLock lock, Condition c,
+                          Thread... threads) {
+        lock.writeLock().lock();
+        assertEquals(threads.length > 0, lock.hasWaiters(c));
+        assertEquals(threads.length, lock.getWaitQueueLength(c));
+        assertEquals(threads.length == 0, lock.getWaitingThreads(c).isEmpty());
+        assertEquals(threads.length, lock.getWaitingThreads(c).size());
+        assertEquals(new HashSet<Thread>(lock.getWaitingThreads(c)),
+                     new HashSet<Thread>(Arrays.asList(threads)));
+        lock.writeLock().unlock();
+    }
+
+    enum AwaitMethod { await, awaitTimed, awaitNanos, awaitUntil }
+
+    /**
+     * Awaits condition "indefinitely" using the specified AwaitMethod.
+     */
+    void await(Condition c, AwaitMethod awaitMethod)
+            throws InterruptedException {
+        long timeoutMillis = 2 * LONG_DELAY_MS;
+        switch (awaitMethod) {
+        case await:
+            c.await();
+            break;
+        case awaitTimed:
+            assertTrue(c.await(timeoutMillis, MILLISECONDS));
+            break;
+        case awaitNanos:
+            long timeoutNanos = MILLISECONDS.toNanos(timeoutMillis);
+            long nanosRemaining = c.awaitNanos(timeoutNanos);
+            assertTrue(nanosRemaining > timeoutNanos / 2);
+            assertTrue(nanosRemaining <= timeoutNanos);
+            break;
+        case awaitUntil:
+            assertTrue(c.awaitUntil(delayedDate(timeoutMillis)));
+            break;
+        default:
+            throw new AssertionError();
+        }
+    }
+
+    /**
+     * Constructor sets given fairness, and is in unlocked state
+     */
+    public void testConstructor() {
+        PublicReentrantReadWriteLock lock;
+
+        lock = new PublicReentrantReadWriteLock();
+        assertFalse(lock.isFair());
+        assertNotWriteLocked(lock);
+        assertEquals(0, lock.getReadLockCount());
+
+        lock = new PublicReentrantReadWriteLock(true);
+        assertTrue(lock.isFair());
+        assertNotWriteLocked(lock);
+        assertEquals(0, lock.getReadLockCount());
+
+        lock = new PublicReentrantReadWriteLock(false);
+        assertFalse(lock.isFair());
+        assertNotWriteLocked(lock);
+        assertEquals(0, lock.getReadLockCount());
+    }
+
+    /**
+     * write-locking and read-locking an unlocked lock succeed
+     */
+    public void testLock()      { testLock(false); }
+    public void testLock_fair() { testLock(true); }
+    public void testLock(boolean fair) {
+        PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        assertNotWriteLocked(lock);
+        lock.writeLock().lock();
+        assertWriteLockedByMoi(lock);
+        lock.writeLock().unlock();
+        assertNotWriteLocked(lock);
+        assertEquals(0, lock.getReadLockCount());
+        lock.readLock().lock();
+        assertNotWriteLocked(lock);
+        assertEquals(1, lock.getReadLockCount());
+        lock.readLock().unlock();
+        assertNotWriteLocked(lock);
+        assertEquals(0, lock.getReadLockCount());
+    }
+
+    /**
+     * getWriteHoldCount returns number of recursive holds
+     */
+    public void testGetWriteHoldCount()      { testGetWriteHoldCount(false); }
+    public void testGetWriteHoldCount_fair() { testGetWriteHoldCount(true); }
+    public void testGetWriteHoldCount(boolean fair) {
+        ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        for (int i = 1; i <= SIZE; i++) {
+            lock.writeLock().lock();
+            assertEquals(i,lock.getWriteHoldCount());
+        }
+        for (int i = SIZE; i > 0; i--) {
+            lock.writeLock().unlock();
+            assertEquals(i - 1,lock.getWriteHoldCount());
+        }
+    }
+
+    /**
+     * writelock.getHoldCount returns number of recursive holds
+     */
+    public void testGetHoldCount()      { testGetHoldCount(false); }
+    public void testGetHoldCount_fair() { testGetHoldCount(true); }
+    public void testGetHoldCount(boolean fair) {
+        ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        for (int i = 1; i <= SIZE; i++) {
+            lock.writeLock().lock();
+            assertEquals(i,lock.writeLock().getHoldCount());
+        }
+        for (int i = SIZE; i > 0; i--) {
+            lock.writeLock().unlock();
+            assertEquals(i - 1,lock.writeLock().getHoldCount());
+        }
+    }
+
+    /**
+     * getReadHoldCount returns number of recursive holds
+     */
+    public void testGetReadHoldCount()      { testGetReadHoldCount(false); }
+    public void testGetReadHoldCount_fair() { testGetReadHoldCount(true); }
+    public void testGetReadHoldCount(boolean fair) {
+        ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        for (int i = 1; i <= SIZE; i++) {
+            lock.readLock().lock();
+            assertEquals(i,lock.getReadHoldCount());
+        }
+        for (int i = SIZE; i > 0; i--) {
+            lock.readLock().unlock();
+            assertEquals(i - 1,lock.getReadHoldCount());
+        }
+    }
+
+    /**
+     * write-unlocking an unlocked lock throws IllegalMonitorStateException
+     */
+    public void testWriteUnlock_IMSE()      { testWriteUnlock_IMSE(false); }
+    public void testWriteUnlock_IMSE_fair() { testWriteUnlock_IMSE(true); }
+    public void testWriteUnlock_IMSE(boolean fair) {
+        ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        try {
+            lock.writeLock().unlock();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * read-unlocking an unlocked lock throws IllegalMonitorStateException
+     */
+    public void testReadUnlock_IMSE()      { testReadUnlock_IMSE(false); }
+    public void testReadUnlock_IMSE_fair() { testReadUnlock_IMSE(true); }
+    public void testReadUnlock_IMSE(boolean fair) {
+        ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        try {
+            lock.readLock().unlock();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * write-lockInterruptibly is interruptible
+     */
+    public void testWriteLockInterruptibly_Interruptible()      { testWriteLockInterruptibly_Interruptible(false); }
+    public void testWriteLockInterruptibly_Interruptible_fair() { testWriteLockInterruptibly_Interruptible(true); }
+    public void testWriteLockInterruptibly_Interruptible(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lockInterruptibly();
+            }});
+
+        waitForQueuedThread(lock, t);
+        t.interrupt();
+        awaitTermination(t);
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * timed write-tryLock is interruptible
+     */
+    public void testWriteTryLock_Interruptible()      { testWriteTryLock_Interruptible(false); }
+    public void testWriteTryLock_Interruptible_fair() { testWriteTryLock_Interruptible(true); }
+    public void testWriteTryLock_Interruptible(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().tryLock(2 * LONG_DELAY_MS, MILLISECONDS);
+            }});
+
+        waitForQueuedThread(lock, t);
+        t.interrupt();
+        awaitTermination(t);
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * read-lockInterruptibly is interruptible
+     */
+    public void testReadLockInterruptibly_Interruptible()      { testReadLockInterruptibly_Interruptible(false); }
+    public void testReadLockInterruptibly_Interruptible_fair() { testReadLockInterruptibly_Interruptible(true); }
+    public void testReadLockInterruptibly_Interruptible(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.readLock().lockInterruptibly();
+            }});
+
+        waitForQueuedThread(lock, t);
+        t.interrupt();
+        awaitTermination(t);
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * timed read-tryLock is interruptible
+     */
+    public void testReadTryLock_Interruptible()      { testReadTryLock_Interruptible(false); }
+    public void testReadTryLock_Interruptible_fair() { testReadTryLock_Interruptible(true); }
+    public void testReadTryLock_Interruptible(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.readLock().tryLock(2 * LONG_DELAY_MS, MILLISECONDS);
+            }});
+
+        waitForQueuedThread(lock, t);
+        t.interrupt();
+        awaitTermination(t);
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * write-tryLock on an unlocked lock succeeds
+     */
+    public void testWriteTryLock()      { testWriteTryLock(false); }
+    public void testWriteTryLock_fair() { testWriteTryLock(true); }
+    public void testWriteTryLock(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        assertTrue(lock.writeLock().tryLock());
+        assertWriteLockedByMoi(lock);
+        assertTrue(lock.writeLock().tryLock());
+        assertWriteLockedByMoi(lock);
+        lock.writeLock().unlock();
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * write-tryLock fails if locked
+     */
+    public void testWriteTryLockWhenLocked()      { testWriteTryLockWhenLocked(false); }
+    public void testWriteTryLockWhenLocked_fair() { testWriteTryLockWhenLocked(true); }
+    public void testWriteTryLockWhenLocked(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertFalse(lock.writeLock().tryLock());
+            }});
+
+        awaitTermination(t);
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * read-tryLock fails if locked
+     */
+    public void testReadTryLockWhenLocked()      { testReadTryLockWhenLocked(false); }
+    public void testReadTryLockWhenLocked_fair() { testReadTryLockWhenLocked(true); }
+    public void testReadTryLockWhenLocked(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertFalse(lock.readLock().tryLock());
+            }});
+
+        awaitTermination(t);
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * Multiple threads can hold a read lock when not write-locked
+     */
+    public void testMultipleReadLocks()      { testMultipleReadLocks(false); }
+    public void testMultipleReadLocks_fair() { testMultipleReadLocks(true); }
+    public void testMultipleReadLocks(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        lock.readLock().lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertTrue(lock.readLock().tryLock());
+                lock.readLock().unlock();
+                assertTrue(lock.readLock().tryLock(LONG_DELAY_MS, MILLISECONDS));
+                lock.readLock().unlock();
+                lock.readLock().lock();
+                lock.readLock().unlock();
+            }});
+
+        awaitTermination(t);
+        lock.readLock().unlock();
+    }
+
+    /**
+     * A writelock succeeds only after a reading thread unlocks
+     */
+    public void testWriteAfterReadLock()      { testWriteAfterReadLock(false); }
+    public void testWriteAfterReadLock_fair() { testWriteAfterReadLock(true); }
+    public void testWriteAfterReadLock(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.readLock().lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertEquals(1, lock.getReadLockCount());
+                lock.writeLock().lock();
+                assertEquals(0, lock.getReadLockCount());
+                lock.writeLock().unlock();
+            }});
+        waitForQueuedThread(lock, t);
+        assertNotWriteLocked(lock);
+        assertEquals(1, lock.getReadLockCount());
+        lock.readLock().unlock();
+        assertEquals(0, lock.getReadLockCount());
+        awaitTermination(t);
+        assertNotWriteLocked(lock);
+    }
+
+    /**
+     * A writelock succeeds only after reading threads unlock
+     */
+    public void testWriteAfterMultipleReadLocks()      { testWriteAfterMultipleReadLocks(false); }
+    public void testWriteAfterMultipleReadLocks_fair() { testWriteAfterMultipleReadLocks(true); }
+    public void testWriteAfterMultipleReadLocks(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.readLock().lock();
+        lock.readLock().lock();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.readLock().lock();
+                assertEquals(3, lock.getReadLockCount());
+                lock.readLock().unlock();
+            }});
+        awaitTermination(t1);
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertEquals(2, lock.getReadLockCount());
+                lock.writeLock().lock();
+                assertEquals(0, lock.getReadLockCount());
+                lock.writeLock().unlock();
+            }});
+        waitForQueuedThread(lock, t2);
+        assertNotWriteLocked(lock);
+        assertEquals(2, lock.getReadLockCount());
+        lock.readLock().unlock();
+        lock.readLock().unlock();
+        assertEquals(0, lock.getReadLockCount());
+        awaitTermination(t2);
+        assertNotWriteLocked(lock);
+    }
+
+    /**
+     * A thread that tries to acquire a fair read lock (non-reentrantly)
+     * will block if there is a waiting writer thread
+     */
+    public void testReaderWriterReaderFairFifo() {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(true);
+        final AtomicBoolean t1GotLock = new AtomicBoolean(false);
+
+        lock.readLock().lock();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertEquals(1, lock.getReadLockCount());
+                lock.writeLock().lock();
+                assertEquals(0, lock.getReadLockCount());
+                t1GotLock.set(true);
+                lock.writeLock().unlock();
+            }});
+        waitForQueuedThread(lock, t1);
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertEquals(1, lock.getReadLockCount());
+                lock.readLock().lock();
+                assertEquals(1, lock.getReadLockCount());
+                assertTrue(t1GotLock.get());
+                lock.readLock().unlock();
+            }});
+        waitForQueuedThread(lock, t2);
+        assertTrue(t1.isAlive());
+        assertNotWriteLocked(lock);
+        assertEquals(1, lock.getReadLockCount());
+        lock.readLock().unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+        assertNotWriteLocked(lock);
+    }
+
+    /**
+     * Readlocks succeed only after a writing thread unlocks
+     */
+    public void testReadAfterWriteLock()      { testReadAfterWriteLock(false); }
+    public void testReadAfterWriteLock_fair() { testReadAfterWriteLock(true); }
+    public void testReadAfterWriteLock(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.readLock().lock();
+                lock.readLock().unlock();
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.readLock().lock();
+                lock.readLock().unlock();
+            }});
+
+        waitForQueuedThread(lock, t1);
+        waitForQueuedThread(lock, t2);
+        releaseWriteLock(lock);
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * Read trylock succeeds if write locked by current thread
+     */
+    public void testReadHoldingWriteLock()      { testReadHoldingWriteLock(false); }
+    public void testReadHoldingWriteLock_fair() { testReadHoldingWriteLock(true); }
+    public void testReadHoldingWriteLock(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        assertTrue(lock.readLock().tryLock());
+        lock.readLock().unlock();
+        lock.writeLock().unlock();
+    }
+
+    /**
+     * Read trylock succeeds (barging) even in the presence of waiting
+     * readers and/or writers
+     */
+    public void testReadTryLockBarging()      { testReadTryLockBarging(false); }
+    public void testReadTryLockBarging_fair() { testReadTryLockBarging(true); }
+    public void testReadTryLockBarging(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.readLock().lock();
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.writeLock().lock();
+                lock.writeLock().unlock();
+            }});
+
+        waitForQueuedThread(lock, t1);
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.readLock().lock();
+                lock.readLock().unlock();
+            }});
+
+        if (fair)
+            waitForQueuedThread(lock, t2);
+
+        Thread t3 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.readLock().tryLock();
+                lock.readLock().unlock();
+            }});
+
+        assertTrue(lock.getReadLockCount() > 0);
+        awaitTermination(t3);
+        assertTrue(t1.isAlive());
+        if (fair) assertTrue(t2.isAlive());
+        lock.readLock().unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * Read lock succeeds if write locked by current thread even if
+     * other threads are waiting for readlock
+     */
+    public void testReadHoldingWriteLock2()      { testReadHoldingWriteLock2(false); }
+    public void testReadHoldingWriteLock2_fair() { testReadHoldingWriteLock2(true); }
+    public void testReadHoldingWriteLock2(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        lock.readLock().lock();
+        lock.readLock().unlock();
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.readLock().lock();
+                lock.readLock().unlock();
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.readLock().lock();
+                lock.readLock().unlock();
+            }});
+
+        waitForQueuedThread(lock, t1);
+        waitForQueuedThread(lock, t2);
+        assertWriteLockedByMoi(lock);
+        lock.readLock().lock();
+        lock.readLock().unlock();
+        releaseWriteLock(lock);
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * Read lock succeeds if write locked by current thread even if
+     * other threads are waiting for writelock
+     */
+    public void testReadHoldingWriteLock3()      { testReadHoldingWriteLock3(false); }
+    public void testReadHoldingWriteLock3_fair() { testReadHoldingWriteLock3(true); }
+    public void testReadHoldingWriteLock3(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        lock.readLock().lock();
+        lock.readLock().unlock();
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.writeLock().lock();
+                lock.writeLock().unlock();
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.writeLock().lock();
+                lock.writeLock().unlock();
+            }});
+
+        waitForQueuedThread(lock, t1);
+        waitForQueuedThread(lock, t2);
+        assertWriteLockedByMoi(lock);
+        lock.readLock().lock();
+        lock.readLock().unlock();
+        assertWriteLockedByMoi(lock);
+        lock.writeLock().unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * Write lock succeeds if write locked by current thread even if
+     * other threads are waiting for writelock
+     */
+    public void testWriteHoldingWriteLock4()      { testWriteHoldingWriteLock4(false); }
+    public void testWriteHoldingWriteLock4_fair() { testWriteHoldingWriteLock4(true); }
+    public void testWriteHoldingWriteLock4(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        lock.writeLock().lock();
+        lock.writeLock().unlock();
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.writeLock().lock();
+                lock.writeLock().unlock();
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.writeLock().lock();
+                lock.writeLock().unlock();
+            }});
+
+        waitForQueuedThread(lock, t1);
+        waitForQueuedThread(lock, t2);
+        assertWriteLockedByMoi(lock);
+        assertEquals(1, lock.getWriteHoldCount());
+        lock.writeLock().lock();
+        assertWriteLockedByMoi(lock);
+        assertEquals(2, lock.getWriteHoldCount());
+        lock.writeLock().unlock();
+        assertWriteLockedByMoi(lock);
+        assertEquals(1, lock.getWriteHoldCount());
+        lock.writeLock().unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * Read tryLock succeeds if readlocked but not writelocked
+     */
+    public void testTryLockWhenReadLocked()      { testTryLockWhenReadLocked(false); }
+    public void testTryLockWhenReadLocked_fair() { testTryLockWhenReadLocked(true); }
+    public void testTryLockWhenReadLocked(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        lock.readLock().lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertTrue(lock.readLock().tryLock());
+                lock.readLock().unlock();
+            }});
+
+        awaitTermination(t);
+        lock.readLock().unlock();
+    }
+
+    /**
+     * write tryLock fails when readlocked
+     */
+    public void testWriteTryLockWhenReadLocked()      { testWriteTryLockWhenReadLocked(false); }
+    public void testWriteTryLockWhenReadLocked_fair() { testWriteTryLockWhenReadLocked(true); }
+    public void testWriteTryLockWhenReadLocked(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        lock.readLock().lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertFalse(lock.writeLock().tryLock());
+            }});
+
+        awaitTermination(t);
+        lock.readLock().unlock();
+    }
+
+    /**
+     * write timed tryLock times out if locked
+     */
+    public void testWriteTryLock_Timeout()      { testWriteTryLock_Timeout(false); }
+    public void testWriteTryLock_Timeout_fair() { testWriteTryLock_Timeout(true); }
+    public void testWriteTryLock_Timeout(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                long timeoutMillis = 10;
+                assertFalse(lock.writeLock().tryLock(timeoutMillis, MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+            }});
+
+        awaitTermination(t);
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * read timed tryLock times out if write-locked
+     */
+    public void testReadTryLock_Timeout()      { testReadTryLock_Timeout(false); }
+    public void testReadTryLock_Timeout_fair() { testReadTryLock_Timeout(true); }
+    public void testReadTryLock_Timeout(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                long timeoutMillis = 10;
+                assertFalse(lock.readLock().tryLock(timeoutMillis, MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+            }});
+
+        awaitTermination(t);
+        assertTrue(lock.writeLock().isHeldByCurrentThread());
+        lock.writeLock().unlock();
+    }
+
+    /**
+     * write lockInterruptibly succeeds if unlocked, else is interruptible
+     */
+    public void testWriteLockInterruptibly()      { testWriteLockInterruptibly(false); }
+    public void testWriteLockInterruptibly_fair() { testWriteLockInterruptibly(true); }
+    public void testWriteLockInterruptibly(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        try {
+            lock.writeLock().lockInterruptibly();
+        } catch (InterruptedException fail) { threadUnexpectedException(fail); }
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lockInterruptibly();
+            }});
+
+        waitForQueuedThread(lock, t);
+        t.interrupt();
+        assertTrue(lock.writeLock().isHeldByCurrentThread());
+        awaitTermination(t);
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * read lockInterruptibly succeeds if lock free else is interruptible
+     */
+    public void testReadLockInterruptibly()      { testReadLockInterruptibly(false); }
+    public void testReadLockInterruptibly_fair() { testReadLockInterruptibly(true); }
+    public void testReadLockInterruptibly(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        try {
+            lock.readLock().lockInterruptibly();
+            lock.readLock().unlock();
+            lock.writeLock().lockInterruptibly();
+        } catch (InterruptedException fail) { threadUnexpectedException(fail); }
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.readLock().lockInterruptibly();
+            }});
+
+        waitForQueuedThread(lock, t);
+        t.interrupt();
+        awaitTermination(t);
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * Calling await without holding lock throws IllegalMonitorStateException
+     */
+    public void testAwait_IMSE()      { testAwait_IMSE(false); }
+    public void testAwait_IMSE_fair() { testAwait_IMSE(true); }
+    public void testAwait_IMSE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        for (AwaitMethod awaitMethod : AwaitMethod.values()) {
+            long startTime = System.nanoTime();
+            try {
+                await(c, awaitMethod);
+                shouldThrow();
+            } catch (IllegalMonitorStateException success) {
+            } catch (InterruptedException fail) {
+                threadUnexpectedException(fail);
+            }
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * Calling signal without holding lock throws IllegalMonitorStateException
+     */
+    public void testSignal_IMSE()      { testSignal_IMSE(false); }
+    public void testSignal_IMSE_fair() { testSignal_IMSE(true); }
+    public void testSignal_IMSE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        try {
+            c.signal();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * Calling signalAll without holding lock throws IllegalMonitorStateException
+     */
+    public void testSignalAll_IMSE()      { testSignalAll_IMSE(false); }
+    public void testSignalAll_IMSE_fair() { testSignalAll_IMSE(true); }
+    public void testSignalAll_IMSE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        try {
+            c.signalAll();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * awaitNanos without a signal times out
+     */
+    public void testAwaitNanos_Timeout()      { testAwaitNanos_Timeout(false); }
+    public void testAwaitNanos_Timeout_fair() { testAwaitNanos_Timeout(true); }
+    public void testAwaitNanos_Timeout(boolean fair) {
+        try {
+            final ReentrantReadWriteLock lock =
+                new ReentrantReadWriteLock(fair);
+            final Condition c = lock.writeLock().newCondition();
+            lock.writeLock().lock();
+            long startTime = System.nanoTime();
+            long timeoutMillis = 10;
+            long timeoutNanos = MILLISECONDS.toNanos(timeoutMillis);
+            long nanosRemaining = c.awaitNanos(timeoutNanos);
+            assertTrue(nanosRemaining <= 0);
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+            lock.writeLock().unlock();
+        } catch (InterruptedException fail) { threadUnexpectedException(fail); }
+    }
+
+    /**
+     * timed await without a signal times out
+     */
+    public void testAwait_Timeout()      { testAwait_Timeout(false); }
+    public void testAwait_Timeout_fair() { testAwait_Timeout(true); }
+    public void testAwait_Timeout(boolean fair) {
+        try {
+            final ReentrantReadWriteLock lock =
+                new ReentrantReadWriteLock(fair);
+            final Condition c = lock.writeLock().newCondition();
+            lock.writeLock().lock();
+            long startTime = System.nanoTime();
+            long timeoutMillis = 10;
+            assertFalse(c.await(timeoutMillis, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+            lock.writeLock().unlock();
+        } catch (InterruptedException fail) { threadUnexpectedException(fail); }
+    }
+
+    /**
+     * awaitUntil without a signal times out
+     */
+    public void testAwaitUntil_Timeout()      { testAwaitUntil_Timeout(false); }
+    public void testAwaitUntil_Timeout_fair() { testAwaitUntil_Timeout(true); }
+    public void testAwaitUntil_Timeout(boolean fair) {
+        try {
+            final ReentrantReadWriteLock lock =
+                new ReentrantReadWriteLock(fair);
+            final Condition c = lock.writeLock().newCondition();
+            lock.writeLock().lock();
+            // We shouldn't assume that nanoTime and currentTimeMillis
+            // use the same time source, so don't use nanoTime here.
+            java.util.Date delayedDate = delayedDate(timeoutMillis());
+            assertFalse(c.awaitUntil(delayedDate));
+            assertTrue(new java.util.Date().getTime() >= delayedDate.getTime());
+            lock.writeLock().unlock();
+        } catch (InterruptedException fail) { threadUnexpectedException(fail); }
+    }
+
+    /**
+     * await returns when signalled
+     */
+    public void testAwait()      { testAwait(false); }
+    public void testAwait_fair() { testAwait(true); }
+    public void testAwait(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final CountDownLatch locked = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lock();
+                locked.countDown();
+                c.await();
+                lock.writeLock().unlock();
+            }});
+
+        await(locked);
+        lock.writeLock().lock();
+        assertHasWaiters(lock, c, t);
+        c.signal();
+        assertHasNoWaiters(lock, c);
+        assertTrue(t.isAlive());
+        lock.writeLock().unlock();
+        awaitTermination(t);
+    }
+
+    /**
+     * awaitUninterruptibly is uninterruptible
+     */
+    public void testAwaitUninterruptibly()      { testAwaitUninterruptibly(false); }
+    public void testAwaitUninterruptibly_fair() { testAwaitUninterruptibly(true); }
+    public void testAwaitUninterruptibly(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(2);
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                // Interrupt before awaitUninterruptibly
+                lock.writeLock().lock();
+                pleaseInterrupt.countDown();
+                Thread.currentThread().interrupt();
+                c.awaitUninterruptibly();
+                assertTrue(Thread.interrupted());
+                lock.writeLock().unlock();
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                // Interrupt during awaitUninterruptibly
+                lock.writeLock().lock();
+                pleaseInterrupt.countDown();
+                c.awaitUninterruptibly();
+                assertTrue(Thread.interrupted());
+                lock.writeLock().unlock();
+            }});
+
+        await(pleaseInterrupt);
+        lock.writeLock().lock();
+        lock.writeLock().unlock();
+        t2.interrupt();
+
+        assertThreadStaysAlive(t1);
+        assertTrue(t2.isAlive());
+
+        lock.writeLock().lock();
+        c.signalAll();
+        lock.writeLock().unlock();
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * await/awaitNanos/awaitUntil is interruptible
+     */
+    public void testInterruptible_await()           { testInterruptible(false, AwaitMethod.await); }
+    public void testInterruptible_await_fair()      { testInterruptible(true,  AwaitMethod.await); }
+    public void testInterruptible_awaitTimed()      { testInterruptible(false, AwaitMethod.awaitTimed); }
+    public void testInterruptible_awaitTimed_fair() { testInterruptible(true,  AwaitMethod.awaitTimed); }
+    public void testInterruptible_awaitNanos()      { testInterruptible(false, AwaitMethod.awaitNanos); }
+    public void testInterruptible_awaitNanos_fair() { testInterruptible(true,  AwaitMethod.awaitNanos); }
+    public void testInterruptible_awaitUntil()      { testInterruptible(false, AwaitMethod.awaitUntil); }
+    public void testInterruptible_awaitUntil_fair() { testInterruptible(true,  AwaitMethod.awaitUntil); }
+    public void testInterruptible(boolean fair, final AwaitMethod awaitMethod) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final CountDownLatch locked = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lock();
+                assertWriteLockedByMoi(lock);
+                assertHasNoWaiters(lock, c);
+                locked.countDown();
+                try {
+                    await(c, awaitMethod);
+                } finally {
+                    assertWriteLockedByMoi(lock);
+                    assertHasNoWaiters(lock, c);
+                    lock.writeLock().unlock();
+                    assertFalse(Thread.interrupted());
+                }
+            }});
+
+        await(locked);
+        assertHasWaiters(lock, c, t);
+        t.interrupt();
+        awaitTermination(t);
+        assertNotWriteLocked(lock);
+    }
+
+    /**
+     * signalAll wakes up all threads
+     */
+    public void testSignalAll_await()           { testSignalAll(false, AwaitMethod.await); }
+    public void testSignalAll_await_fair()      { testSignalAll(true,  AwaitMethod.await); }
+    public void testSignalAll_awaitTimed()      { testSignalAll(false, AwaitMethod.awaitTimed); }
+    public void testSignalAll_awaitTimed_fair() { testSignalAll(true,  AwaitMethod.awaitTimed); }
+    public void testSignalAll_awaitNanos()      { testSignalAll(false, AwaitMethod.awaitNanos); }
+    public void testSignalAll_awaitNanos_fair() { testSignalAll(true,  AwaitMethod.awaitNanos); }
+    public void testSignalAll_awaitUntil()      { testSignalAll(false, AwaitMethod.awaitUntil); }
+    public void testSignalAll_awaitUntil_fair() { testSignalAll(true,  AwaitMethod.awaitUntil); }
+    public void testSignalAll(boolean fair, final AwaitMethod awaitMethod) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final CountDownLatch locked = new CountDownLatch(2);
+        final Lock writeLock = lock.writeLock();
+        class Awaiter extends CheckedRunnable {
+            public void realRun() throws InterruptedException {
+                writeLock.lock();
+                locked.countDown();
+                await(c, awaitMethod);
+                writeLock.unlock();
+            }
+        }
+
+        Thread t1 = newStartedThread(new Awaiter());
+        Thread t2 = newStartedThread(new Awaiter());
+
+        await(locked);
+        writeLock.lock();
+        assertHasWaiters(lock, c, t1, t2);
+        c.signalAll();
+        assertHasNoWaiters(lock, c);
+        writeLock.unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * signal wakes up waiting threads in FIFO order
+     */
+    public void testSignalWakesFifo()      { testSignalWakesFifo(false); }
+    public void testSignalWakesFifo_fair() { testSignalWakesFifo(true); }
+    public void testSignalWakesFifo(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final CountDownLatch locked1 = new CountDownLatch(1);
+        final CountDownLatch locked2 = new CountDownLatch(1);
+        final Lock writeLock = lock.writeLock();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                writeLock.lock();
+                locked1.countDown();
+                c.await();
+                writeLock.unlock();
+            }});
+
+        await(locked1);
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                writeLock.lock();
+                locked2.countDown();
+                c.await();
+                writeLock.unlock();
+            }});
+
+        await(locked2);
+
+        writeLock.lock();
+        assertHasWaiters(lock, c, t1, t2);
+        assertFalse(lock.hasQueuedThreads());
+        c.signal();
+        assertHasWaiters(lock, c, t2);
+        assertTrue(lock.hasQueuedThread(t1));
+        assertFalse(lock.hasQueuedThread(t2));
+        c.signal();
+        assertHasNoWaiters(lock, c);
+        assertTrue(lock.hasQueuedThread(t1));
+        assertTrue(lock.hasQueuedThread(t2));
+        writeLock.unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * await after multiple reentrant locking preserves lock count
+     */
+    public void testAwaitLockCount()      { testAwaitLockCount(false); }
+    public void testAwaitLockCount_fair() { testAwaitLockCount(true); }
+    public void testAwaitLockCount(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final CountDownLatch locked = new CountDownLatch(2);
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lock();
+                assertWriteLockedByMoi(lock);
+                assertEquals(1, lock.writeLock().getHoldCount());
+                locked.countDown();
+                c.await();
+                assertWriteLockedByMoi(lock);
+                assertEquals(1, lock.writeLock().getHoldCount());
+                lock.writeLock().unlock();
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lock();
+                lock.writeLock().lock();
+                assertWriteLockedByMoi(lock);
+                assertEquals(2, lock.writeLock().getHoldCount());
+                locked.countDown();
+                c.await();
+                assertWriteLockedByMoi(lock);
+                assertEquals(2, lock.writeLock().getHoldCount());
+                lock.writeLock().unlock();
+                lock.writeLock().unlock();
+            }});
+
+        await(locked);
+        lock.writeLock().lock();
+        assertHasWaiters(lock, c, t1, t2);
+        c.signalAll();
+        assertHasNoWaiters(lock, c);
+        lock.writeLock().unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * A serialized lock deserializes as unlocked
+     */
+    public void testSerialization()      { testSerialization(false); }
+    public void testSerialization_fair() { testSerialization(true); }
+    public void testSerialization(boolean fair) {
+        ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        lock.readLock().lock();
+
+        ReentrantReadWriteLock clone = serialClone(lock);
+        assertEquals(lock.isFair(), clone.isFair());
+        assertTrue(lock.isWriteLocked());
+        assertFalse(clone.isWriteLocked());
+        assertEquals(1, lock.getReadLockCount());
+        assertEquals(0, clone.getReadLockCount());
+        clone.writeLock().lock();
+        clone.readLock().lock();
+        assertTrue(clone.isWriteLocked());
+        assertEquals(1, clone.getReadLockCount());
+        clone.readLock().unlock();
+        clone.writeLock().unlock();
+        assertFalse(clone.isWriteLocked());
+        assertEquals(1, lock.getReadLockCount());
+        assertEquals(0, clone.getReadLockCount());
+    }
+
+    /**
+     * hasQueuedThreads reports whether there are waiting threads
+     */
+    public void testHasQueuedThreads()      { testHasQueuedThreads(false); }
+    public void testHasQueuedThreads_fair() { testHasQueuedThreads(true); }
+    public void testHasQueuedThreads(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        Thread t1 = new Thread(new InterruptedLockRunnable(lock));
+        Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
+        assertFalse(lock.hasQueuedThreads());
+        lock.writeLock().lock();
+        assertFalse(lock.hasQueuedThreads());
+        t1.start();
+        waitForQueuedThread(lock, t1);
+        assertTrue(lock.hasQueuedThreads());
+        t2.start();
+        waitForQueuedThread(lock, t2);
+        assertTrue(lock.hasQueuedThreads());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertTrue(lock.hasQueuedThreads());
+        lock.writeLock().unlock();
+        awaitTermination(t2);
+        assertFalse(lock.hasQueuedThreads());
+    }
+
+    /**
+     * hasQueuedThread(null) throws NPE
+     */
+    public void testHasQueuedThreadNPE()      { testHasQueuedThreadNPE(false); }
+    public void testHasQueuedThreadNPE_fair() { testHasQueuedThreadNPE(true); }
+    public void testHasQueuedThreadNPE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        try {
+            lock.hasQueuedThread(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * hasQueuedThread reports whether a thread is queued
+     */
+    public void testHasQueuedThread()      { testHasQueuedThread(false); }
+    public void testHasQueuedThread_fair() { testHasQueuedThread(true); }
+    public void testHasQueuedThread(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        Thread t1 = new Thread(new InterruptedLockRunnable(lock));
+        Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
+        assertFalse(lock.hasQueuedThread(t1));
+        assertFalse(lock.hasQueuedThread(t2));
+        lock.writeLock().lock();
+        t1.start();
+        waitForQueuedThread(lock, t1);
+        assertTrue(lock.hasQueuedThread(t1));
+        assertFalse(lock.hasQueuedThread(t2));
+        t2.start();
+        waitForQueuedThread(lock, t2);
+        assertTrue(lock.hasQueuedThread(t1));
+        assertTrue(lock.hasQueuedThread(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertFalse(lock.hasQueuedThread(t1));
+        assertTrue(lock.hasQueuedThread(t2));
+        lock.writeLock().unlock();
+        awaitTermination(t2);
+        assertFalse(lock.hasQueuedThread(t1));
+        assertFalse(lock.hasQueuedThread(t2));
+    }
+
+    /**
+     * getQueueLength reports number of waiting threads
+     */
+    public void testGetQueueLength()      { testGetQueueLength(false); }
+    public void testGetQueueLength_fair() { testGetQueueLength(true); }
+    public void testGetQueueLength(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        Thread t1 = new Thread(new InterruptedLockRunnable(lock));
+        Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
+        assertEquals(0, lock.getQueueLength());
+        lock.writeLock().lock();
+        t1.start();
+        waitForQueuedThread(lock, t1);
+        assertEquals(1, lock.getQueueLength());
+        t2.start();
+        waitForQueuedThread(lock, t2);
+        assertEquals(2, lock.getQueueLength());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertEquals(1, lock.getQueueLength());
+        lock.writeLock().unlock();
+        awaitTermination(t2);
+        assertEquals(0, lock.getQueueLength());
+    }
+
+    /**
+     * getQueuedThreads includes waiting threads
+     */
+    public void testGetQueuedThreads()      { testGetQueuedThreads(false); }
+    public void testGetQueuedThreads_fair() { testGetQueuedThreads(true); }
+    public void testGetQueuedThreads(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        Thread t1 = new Thread(new InterruptedLockRunnable(lock));
+        Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
+        assertTrue(lock.getQueuedThreads().isEmpty());
+        lock.writeLock().lock();
+        assertTrue(lock.getQueuedThreads().isEmpty());
+        t1.start();
+        waitForQueuedThread(lock, t1);
+        assertEquals(1, lock.getQueuedThreads().size());
+        assertTrue(lock.getQueuedThreads().contains(t1));
+        t2.start();
+        waitForQueuedThread(lock, t2);
+        assertEquals(2, lock.getQueuedThreads().size());
+        assertTrue(lock.getQueuedThreads().contains(t1));
+        assertTrue(lock.getQueuedThreads().contains(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertFalse(lock.getQueuedThreads().contains(t1));
+        assertTrue(lock.getQueuedThreads().contains(t2));
+        assertEquals(1, lock.getQueuedThreads().size());
+        lock.writeLock().unlock();
+        awaitTermination(t2);
+        assertTrue(lock.getQueuedThreads().isEmpty());
+    }
+
+    /**
+     * hasWaiters throws NPE if null
+     */
+    public void testHasWaitersNPE()      { testHasWaitersNPE(false); }
+    public void testHasWaitersNPE_fair() { testHasWaitersNPE(true); }
+    public void testHasWaitersNPE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        try {
+            lock.hasWaiters(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * getWaitQueueLength throws NPE if null
+     */
+    public void testGetWaitQueueLengthNPE()      { testGetWaitQueueLengthNPE(false); }
+    public void testGetWaitQueueLengthNPE_fair() { testGetWaitQueueLengthNPE(true); }
+    public void testGetWaitQueueLengthNPE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        try {
+            lock.getWaitQueueLength(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * getWaitingThreads throws NPE if null
+     */
+    public void testGetWaitingThreadsNPE()      { testGetWaitingThreadsNPE(false); }
+    public void testGetWaitingThreadsNPE_fair() { testGetWaitingThreadsNPE(true); }
+    public void testGetWaitingThreadsNPE(boolean fair) {
+        final PublicReentrantReadWriteLock lock = new PublicReentrantReadWriteLock(fair);
+        try {
+            lock.getWaitingThreads(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * hasWaiters throws IllegalArgumentException if not owned
+     */
+    public void testHasWaitersIAE()      { testHasWaitersIAE(false); }
+    public void testHasWaitersIAE_fair() { testHasWaitersIAE(true); }
+    public void testHasWaitersIAE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final ReentrantReadWriteLock lock2 = new ReentrantReadWriteLock(fair);
+        try {
+            lock2.hasWaiters(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * hasWaiters throws IllegalMonitorStateException if not locked
+     */
+    public void testHasWaitersIMSE()      { testHasWaitersIMSE(false); }
+    public void testHasWaitersIMSE_fair() { testHasWaitersIMSE(true); }
+    public void testHasWaitersIMSE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        try {
+            lock.hasWaiters(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * getWaitQueueLength throws IllegalArgumentException if not owned
+     */
+    public void testGetWaitQueueLengthIAE()      { testGetWaitQueueLengthIAE(false); }
+    public void testGetWaitQueueLengthIAE_fair() { testGetWaitQueueLengthIAE(true); }
+    public void testGetWaitQueueLengthIAE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final ReentrantReadWriteLock lock2 = new ReentrantReadWriteLock(fair);
+        try {
+            lock2.getWaitQueueLength(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * getWaitQueueLength throws IllegalMonitorStateException if not locked
+     */
+    public void testGetWaitQueueLengthIMSE()      { testGetWaitQueueLengthIMSE(false); }
+    public void testGetWaitQueueLengthIMSE_fair() { testGetWaitQueueLengthIMSE(true); }
+    public void testGetWaitQueueLengthIMSE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        try {
+            lock.getWaitQueueLength(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * getWaitingThreads throws IllegalArgumentException if not owned
+     */
+    public void testGetWaitingThreadsIAE()      { testGetWaitingThreadsIAE(false); }
+    public void testGetWaitingThreadsIAE_fair() { testGetWaitingThreadsIAE(true); }
+    public void testGetWaitingThreadsIAE(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final PublicReentrantReadWriteLock lock2 =
+            new PublicReentrantReadWriteLock(fair);
+        try {
+            lock2.getWaitingThreads(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * getWaitingThreads throws IllegalMonitorStateException if not locked
+     */
+    public void testGetWaitingThreadsIMSE()      { testGetWaitingThreadsIMSE(false); }
+    public void testGetWaitingThreadsIMSE_fair() { testGetWaitingThreadsIMSE(true); }
+    public void testGetWaitingThreadsIMSE(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        try {
+            lock.getWaitingThreads(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * hasWaiters returns true when a thread is waiting, else false
+     */
+    public void testHasWaiters()      { testHasWaiters(false); }
+    public void testHasWaiters_fair() { testHasWaiters(true); }
+    public void testHasWaiters(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final CountDownLatch locked = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lock();
+                assertHasNoWaiters(lock, c);
+                assertFalse(lock.hasWaiters(c));
+                locked.countDown();
+                c.await();
+                assertHasNoWaiters(lock, c);
+                assertFalse(lock.hasWaiters(c));
+                lock.writeLock().unlock();
+            }});
+
+        await(locked);
+        lock.writeLock().lock();
+        assertHasWaiters(lock, c, t);
+        assertTrue(lock.hasWaiters(c));
+        c.signal();
+        assertHasNoWaiters(lock, c);
+        assertFalse(lock.hasWaiters(c));
+        lock.writeLock().unlock();
+        awaitTermination(t);
+        assertHasNoWaiters(lock, c);
+    }
+
+    /**
+     * getWaitQueueLength returns number of waiting threads
+     */
+    public void testGetWaitQueueLength()      { testGetWaitQueueLength(false); }
+    public void testGetWaitQueueLength_fair() { testGetWaitQueueLength(true); }
+    public void testGetWaitQueueLength(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final CountDownLatch locked = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lock();
+                assertEquals(0, lock.getWaitQueueLength(c));
+                locked.countDown();
+                c.await();
+                lock.writeLock().unlock();
+            }});
+
+        await(locked);
+        lock.writeLock().lock();
+        assertHasWaiters(lock, c, t);
+        assertEquals(1, lock.getWaitQueueLength(c));
+        c.signal();
+        assertHasNoWaiters(lock, c);
+        assertEquals(0, lock.getWaitQueueLength(c));
+        lock.writeLock().unlock();
+        awaitTermination(t);
+    }
+
+    /**
+     * getWaitingThreads returns only and all waiting threads
+     */
+    public void testGetWaitingThreads()      { testGetWaitingThreads(false); }
+    public void testGetWaitingThreads_fair() { testGetWaitingThreads(true); }
+    public void testGetWaitingThreads(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final CountDownLatch locked1 = new CountDownLatch(1);
+        final CountDownLatch locked2 = new CountDownLatch(1);
+        Thread t1 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lock();
+                assertTrue(lock.getWaitingThreads(c).isEmpty());
+                locked1.countDown();
+                c.await();
+                lock.writeLock().unlock();
+            }});
+
+        Thread t2 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lock();
+                assertFalse(lock.getWaitingThreads(c).isEmpty());
+                locked2.countDown();
+                c.await();
+                lock.writeLock().unlock();
+            }});
+
+        lock.writeLock().lock();
+        assertTrue(lock.getWaitingThreads(c).isEmpty());
+        lock.writeLock().unlock();
+
+        t1.start();
+        await(locked1);
+        t2.start();
+        await(locked2);
+
+        lock.writeLock().lock();
+        assertTrue(lock.hasWaiters(c));
+        assertTrue(lock.getWaitingThreads(c).contains(t1));
+        assertTrue(lock.getWaitingThreads(c).contains(t2));
+        assertEquals(2, lock.getWaitingThreads(c).size());
+        c.signalAll();
+        assertHasNoWaiters(lock, c);
+        lock.writeLock().unlock();
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+
+        assertHasNoWaiters(lock, c);
+    }
+
+    /**
+     * toString indicates current lock state
+     */
+    public void testToString()      { testToString(false); }
+    public void testToString_fair() { testToString(true); }
+    public void testToString(boolean fair) {
+        ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        assertTrue(lock.toString().contains("Write locks = 0"));
+        assertTrue(lock.toString().contains("Read locks = 0"));
+        lock.writeLock().lock();
+        assertTrue(lock.toString().contains("Write locks = 1"));
+        assertTrue(lock.toString().contains("Read locks = 0"));
+        lock.writeLock().unlock();
+        lock.readLock().lock();
+        lock.readLock().lock();
+        assertTrue(lock.toString().contains("Write locks = 0"));
+        assertTrue(lock.toString().contains("Read locks = 2"));
+    }
+
+    /**
+     * readLock.toString indicates current lock state
+     */
+    public void testReadLockToString()      { testReadLockToString(false); }
+    public void testReadLockToString_fair() { testReadLockToString(true); }
+    public void testReadLockToString(boolean fair) {
+        ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        assertTrue(lock.readLock().toString().contains("Read locks = 0"));
+        lock.readLock().lock();
+        lock.readLock().lock();
+        assertTrue(lock.readLock().toString().contains("Read locks = 2"));
+    }
+
+    /**
+     * writeLock.toString indicates current lock state
+     */
+    public void testWriteLockToString()      { testWriteLockToString(false); }
+    public void testWriteLockToString_fair() { testWriteLockToString(true); }
+    public void testWriteLockToString(boolean fair) {
+        ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        assertTrue(lock.writeLock().toString().contains("Unlocked"));
+        lock.writeLock().lock();
+        assertTrue(lock.writeLock().toString().contains("Locked"));
+        lock.writeLock().unlock();
+        assertTrue(lock.writeLock().toString().contains("Unlocked"));
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/ScheduledExecutorSubclassTest.java b/jdk/test/java/util/concurrent/tck/ScheduledExecutorSubclassTest.java
new file mode 100644
index 00000000000..50b7b171140
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/ScheduledExecutorSubclassTest.java
@@ -0,0 +1,1286 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Delayed;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.RunnableScheduledFuture;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ScheduledExecutorSubclassTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ScheduledExecutorSubclassTest.class);
+    }
+
+    static class CustomTask<V> implements RunnableScheduledFuture<V> {
+        RunnableScheduledFuture<V> task;
+        volatile boolean ran;
+        CustomTask(RunnableScheduledFuture<V> t) { task = t; }
+        public boolean isPeriodic() { return task.isPeriodic(); }
+        public void run() {
+            ran = true;
+            task.run();
+        }
+        public long getDelay(TimeUnit unit) { return task.getDelay(unit); }
+        public int compareTo(Delayed t) {
+            return task.compareTo(((CustomTask)t).task);
+        }
+        public boolean cancel(boolean mayInterruptIfRunning) {
+            return task.cancel(mayInterruptIfRunning);
+        }
+        public boolean isCancelled() { return task.isCancelled(); }
+        public boolean isDone() { return task.isDone(); }
+        public V get() throws InterruptedException, ExecutionException {
+            V v = task.get();
+            assertTrue(ran);
+            return v;
+        }
+        public V get(long time, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+            V v = task.get(time, unit);
+            assertTrue(ran);
+            return v;
+        }
+    }
+
+    public class CustomExecutor extends ScheduledThreadPoolExecutor {
+
+        protected <V> RunnableScheduledFuture<V> decorateTask(Runnable r, RunnableScheduledFuture<V> task) {
+            return new CustomTask<V>(task);
+        }
+
+        protected <V> RunnableScheduledFuture<V> decorateTask(Callable<V> c, RunnableScheduledFuture<V> task) {
+            return new CustomTask<V>(task);
+        }
+        CustomExecutor(int corePoolSize) { super(corePoolSize); }
+        CustomExecutor(int corePoolSize, RejectedExecutionHandler handler) {
+            super(corePoolSize, handler);
+        }
+
+        CustomExecutor(int corePoolSize, ThreadFactory threadFactory) {
+            super(corePoolSize, threadFactory);
+        }
+        CustomExecutor(int corePoolSize, ThreadFactory threadFactory,
+                       RejectedExecutionHandler handler) {
+            super(corePoolSize, threadFactory, handler);
+        }
+
+    }
+
+    /**
+     * execute successfully executes a runnable
+     */
+    public void testExecute() throws InterruptedException {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch done = new CountDownLatch(1);
+            final Runnable task = new CheckedRunnable() {
+                public void realRun() { done.countDown(); }};
+            p.execute(task);
+            await(done);
+        }
+    }
+
+    /**
+     * delayed schedule of callable successfully executes after delay
+     */
+    public void testSchedule1() throws Exception {
+        final CountDownLatch done = new CountDownLatch(1);
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final long startTime = System.nanoTime();
+            Callable task = new CheckedCallable<Boolean>() {
+                public Boolean realCall() {
+                    done.countDown();
+                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                    return Boolean.TRUE;
+                }};
+            Future f = p.schedule(task, timeoutMillis(), MILLISECONDS);
+            assertSame(Boolean.TRUE, f.get());
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        }
+    }
+
+    /**
+     * delayed schedule of runnable successfully executes after delay
+     */
+    public void testSchedule3() throws Exception {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final long startTime = System.nanoTime();
+            final CountDownLatch done = new CountDownLatch(1);
+            Runnable task = new CheckedRunnable() {
+                public void realRun() {
+                    done.countDown();
+                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                }};
+            Future f = p.schedule(task, timeoutMillis(), MILLISECONDS);
+            await(done);
+            assertNull(f.get(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        }
+    }
+
+    /**
+     * scheduleAtFixedRate executes runnable after given initial delay
+     */
+    public void testSchedule4() throws InterruptedException {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final long startTime = System.nanoTime();
+            final CountDownLatch done = new CountDownLatch(1);
+            Runnable task = new CheckedRunnable() {
+                public void realRun() {
+                    done.countDown();
+                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                }};
+            ScheduledFuture f =
+                p.scheduleAtFixedRate(task, timeoutMillis(),
+                                      LONG_DELAY_MS, MILLISECONDS);
+            await(done);
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            f.cancel(true);
+        }
+    }
+
+    /**
+     * scheduleWithFixedDelay executes runnable after given initial delay
+     */
+    public void testSchedule5() throws InterruptedException {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final long startTime = System.nanoTime();
+            final CountDownLatch done = new CountDownLatch(1);
+            Runnable task = new CheckedRunnable() {
+                public void realRun() {
+                    done.countDown();
+                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                }};
+            ScheduledFuture f =
+                p.scheduleWithFixedDelay(task, timeoutMillis(),
+                                         LONG_DELAY_MS, MILLISECONDS);
+            await(done);
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            f.cancel(true);
+        }
+    }
+
+    static class RunnableCounter implements Runnable {
+        AtomicInteger count = new AtomicInteger(0);
+        public void run() { count.getAndIncrement(); }
+    }
+
+    /**
+     * scheduleAtFixedRate executes series of tasks at given rate
+     */
+    public void testFixedRateSequence() throws InterruptedException {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            for (int delay = 1; delay <= LONG_DELAY_MS; delay *= 3) {
+                long startTime = System.nanoTime();
+                int cycles = 10;
+                final CountDownLatch done = new CountDownLatch(cycles);
+                Runnable task = new CheckedRunnable() {
+                    public void realRun() { done.countDown(); }};
+                ScheduledFuture h =
+                    p.scheduleAtFixedRate(task, 0, delay, MILLISECONDS);
+                await(done);
+                h.cancel(true);
+                double normalizedTime =
+                    (double) millisElapsedSince(startTime) / delay;
+                if (normalizedTime >= cycles - 1 &&
+                    normalizedTime <= cycles)
+                    return;
+            }
+            fail("unexpected execution rate");
+        }
+    }
+
+    /**
+     * scheduleWithFixedDelay executes series of tasks with given period
+     */
+    public void testFixedDelaySequence() throws InterruptedException {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            for (int delay = 1; delay <= LONG_DELAY_MS; delay *= 3) {
+                long startTime = System.nanoTime();
+                int cycles = 10;
+                final CountDownLatch done = new CountDownLatch(cycles);
+                Runnable task = new CheckedRunnable() {
+                    public void realRun() { done.countDown(); }};
+                ScheduledFuture h =
+                    p.scheduleWithFixedDelay(task, 0, delay, MILLISECONDS);
+                await(done);
+                h.cancel(true);
+                double normalizedTime =
+                    (double) millisElapsedSince(startTime) / delay;
+                if (normalizedTime >= cycles - 1 &&
+                    normalizedTime <= cycles)
+                    return;
+            }
+            fail("unexpected execution rate");
+        }
+    }
+
+    /**
+     * execute(null) throws NPE
+     */
+    public void testExecuteNull() throws InterruptedException {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.execute(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * schedule(null) throws NPE
+     */
+    public void testScheduleNull() throws InterruptedException {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                TrackedCallable callable = null;
+                Future f = p.schedule(callable, SHORT_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * execute throws RejectedExecutionException if shutdown
+     */
+    public void testSchedule1_RejectedExecutionException() {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.shutdown();
+                p.schedule(new NoOpRunnable(),
+                           MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (RejectedExecutionException success) {
+            } catch (SecurityException ok) {}
+        }
+    }
+
+    /**
+     * schedule throws RejectedExecutionException if shutdown
+     */
+    public void testSchedule2_RejectedExecutionException() {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.shutdown();
+                p.schedule(new NoOpCallable(),
+                           MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (RejectedExecutionException success) {
+            } catch (SecurityException ok) {}
+        }
+    }
+
+    /**
+     * schedule callable throws RejectedExecutionException if shutdown
+     */
+    public void testSchedule3_RejectedExecutionException() {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.shutdown();
+                p.schedule(new NoOpCallable(),
+                           MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (RejectedExecutionException success) {
+            } catch (SecurityException ok) {}
+        }
+    }
+
+    /**
+     * scheduleAtFixedRate throws RejectedExecutionException if shutdown
+     */
+    public void testScheduleAtFixedRate1_RejectedExecutionException() {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.shutdown();
+                p.scheduleAtFixedRate(new NoOpRunnable(),
+                                      MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (RejectedExecutionException success) {
+            } catch (SecurityException ok) {}
+        }
+    }
+
+    /**
+     * scheduleWithFixedDelay throws RejectedExecutionException if shutdown
+     */
+    public void testScheduleWithFixedDelay1_RejectedExecutionException() {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.shutdown();
+                p.scheduleWithFixedDelay(new NoOpRunnable(),
+                                         MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (RejectedExecutionException success) {
+            } catch (SecurityException ok) {}
+        }
+    }
+
+    /**
+     * getActiveCount increases but doesn't overestimate, when a
+     * thread becomes active
+     */
+    public void testGetActiveCount() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            assertEquals(0, p.getActiveCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getActiveCount());
+                    await(done);
+                }});
+            await(threadStarted);
+            assertEquals(1, p.getActiveCount());
+        }
+    }
+
+    /**
+     * getCompletedTaskCount increases, but doesn't overestimate,
+     * when tasks complete
+     */
+    public void testGetCompletedTaskCount() throws InterruptedException {
+        final ThreadPoolExecutor p = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            final CountDownLatch threadProceed = new CountDownLatch(1);
+            final CountDownLatch threadDone = new CountDownLatch(1);
+            assertEquals(0, p.getCompletedTaskCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(0, p.getCompletedTaskCount());
+                    threadProceed.await();
+                    threadDone.countDown();
+                }});
+            await(threadStarted);
+            assertEquals(0, p.getCompletedTaskCount());
+            threadProceed.countDown();
+            threadDone.await();
+            long startTime = System.nanoTime();
+            while (p.getCompletedTaskCount() != 1) {
+                if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                    fail("timed out");
+                Thread.yield();
+            }
+        }
+    }
+
+    /**
+     * getCorePoolSize returns size given in constructor if not otherwise set
+     */
+    public void testGetCorePoolSize() {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertEquals(1, p.getCorePoolSize());
+        }
+    }
+
+    /**
+     * getLargestPoolSize increases, but doesn't overestimate, when
+     * multiple threads active
+     */
+    public void testGetLargestPoolSize() throws InterruptedException {
+        final int THREADS = 3;
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p = new CustomExecutor(THREADS);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadsStarted = new CountDownLatch(THREADS);
+            assertEquals(0, p.getLargestPoolSize());
+            for (int i = 0; i < THREADS; i++)
+                p.execute(new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadsStarted.countDown();
+                        await(done);
+                        assertEquals(THREADS, p.getLargestPoolSize());
+                    }});
+            await(threadsStarted);
+            assertEquals(THREADS, p.getLargestPoolSize());
+        }
+        assertEquals(THREADS, p.getLargestPoolSize());
+    }
+
+    /**
+     * getPoolSize increases, but doesn't overestimate, when threads
+     * become active
+     */
+    public void testGetPoolSize() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            assertEquals(0, p.getPoolSize());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getPoolSize());
+                    await(done);
+                }});
+            await(threadStarted);
+            assertEquals(1, p.getPoolSize());
+        }
+    }
+
+    /**
+     * getTaskCount increases, but doesn't overestimate, when tasks
+     * submitted
+     */
+    public void testGetTaskCount() throws InterruptedException {
+        final int TASKS = 3;
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            assertEquals(0, p.getTaskCount());
+            assertEquals(0, p.getCompletedTaskCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    await(done);
+                }});
+            await(threadStarted);
+            assertEquals(1, p.getTaskCount());
+            assertEquals(0, p.getCompletedTaskCount());
+            for (int i = 0; i < TASKS; i++) {
+                assertEquals(1 + i, p.getTaskCount());
+                p.execute(new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadStarted.countDown();
+                        assertEquals(1 + TASKS, p.getTaskCount());
+                        await(done);
+                    }});
+            }
+            assertEquals(1 + TASKS, p.getTaskCount());
+            assertEquals(0, p.getCompletedTaskCount());
+        }
+        assertEquals(1 + TASKS, p.getTaskCount());
+        assertEquals(1 + TASKS, p.getCompletedTaskCount());
+    }
+
+    /**
+     * getThreadFactory returns factory in constructor if not set
+     */
+    public void testGetThreadFactory() {
+        final ThreadFactory threadFactory = new SimpleThreadFactory();
+        final CustomExecutor p = new CustomExecutor(1, threadFactory);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertSame(threadFactory, p.getThreadFactory());
+        }
+    }
+
+    /**
+     * setThreadFactory sets the thread factory returned by getThreadFactory
+     */
+    public void testSetThreadFactory() {
+        final ThreadFactory threadFactory = new SimpleThreadFactory();
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            p.setThreadFactory(threadFactory);
+            assertSame(threadFactory, p.getThreadFactory());
+        }
+    }
+
+    /**
+     * setThreadFactory(null) throws NPE
+     */
+    public void testSetThreadFactoryNull() {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.setThreadFactory(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * isShutdown is false before shutdown, true after
+     */
+    public void testIsShutdown() {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertFalse(p.isShutdown());
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertTrue(p.isShutdown());
+        }
+    }
+
+    /**
+     * isTerminated is false before termination, true after
+     */
+    public void testIsTerminated() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(p.isTerminated());
+                    threadStarted.countDown();
+                    await(done);
+                }});
+            await(threadStarted);
+            assertFalse(p.isTerminated());
+            assertFalse(p.isTerminating());
+            done.countDown();
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(p.isTerminated());
+        }
+    }
+
+    /**
+     * isTerminating is not true when running or when terminated
+     */
+    public void testIsTerminating() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            assertFalse(p.isTerminating());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(p.isTerminating());
+                    threadStarted.countDown();
+                    await(done);
+                }});
+            await(threadStarted);
+            assertFalse(p.isTerminating());
+            done.countDown();
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(p.isTerminated());
+            assertFalse(p.isTerminating());
+        }
+    }
+
+    /**
+     * getQueue returns the work queue, which contains queued tasks
+     */
+    public void testGetQueue() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ScheduledThreadPoolExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            ScheduledFuture[] tasks = new ScheduledFuture[5];
+            for (int i = 0; i < tasks.length; i++) {
+                Runnable r = new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadStarted.countDown();
+                        await(done);
+                    }};
+                tasks[i] = p.schedule(r, 1, MILLISECONDS);
+            }
+            await(threadStarted);
+            BlockingQueue<Runnable> q = p.getQueue();
+            assertTrue(q.contains(tasks[tasks.length - 1]));
+            assertFalse(q.contains(tasks[0]));
+        }
+    }
+
+    /**
+     * remove(task) removes queued task, and fails to remove active task
+     */
+    public void testRemove() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ScheduledThreadPoolExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            ScheduledFuture[] tasks = new ScheduledFuture[5];
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            for (int i = 0; i < tasks.length; i++) {
+                Runnable r = new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadStarted.countDown();
+                        await(done);
+                    }};
+                tasks[i] = p.schedule(r, 1, MILLISECONDS);
+            }
+            await(threadStarted);
+            BlockingQueue<Runnable> q = p.getQueue();
+            assertFalse(p.remove((Runnable)tasks[0]));
+            assertTrue(q.contains((Runnable)tasks[4]));
+            assertTrue(q.contains((Runnable)tasks[3]));
+            assertTrue(p.remove((Runnable)tasks[4]));
+            assertFalse(p.remove((Runnable)tasks[4]));
+            assertFalse(q.contains((Runnable)tasks[4]));
+            assertTrue(q.contains((Runnable)tasks[3]));
+            assertTrue(p.remove((Runnable)tasks[3]));
+            assertFalse(q.contains((Runnable)tasks[3]));
+        }
+    }
+
+    /**
+     * purge removes cancelled tasks from the queue
+     */
+    public void testPurge() throws InterruptedException {
+        final ScheduledFuture[] tasks = new ScheduledFuture[5];
+        final Runnable releaser = new Runnable() { public void run() {
+            for (ScheduledFuture task : tasks)
+                if (task != null) task.cancel(true); }};
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p, releaser)) {
+            for (int i = 0; i < tasks.length; i++)
+                tasks[i] = p.schedule(new SmallPossiblyInterruptedRunnable(),
+                                      LONG_DELAY_MS, MILLISECONDS);
+            int max = tasks.length;
+            if (tasks[4].cancel(true)) --max;
+            if (tasks[3].cancel(true)) --max;
+            // There must eventually be an interference-free point at
+            // which purge will not fail. (At worst, when queue is empty.)
+            long startTime = System.nanoTime();
+            do {
+                p.purge();
+                long count = p.getTaskCount();
+                if (count == max)
+                    return;
+            } while (millisElapsedSince(startTime) < LONG_DELAY_MS);
+            fail("Purge failed to remove cancelled tasks");
+        }
+    }
+
+    /**
+     * shutdownNow returns a list containing tasks that were not run,
+     * and those tasks are drained from the queue
+     */
+    public void testShutdownNow() throws InterruptedException {
+        final int poolSize = 2;
+        final int count = 5;
+        final AtomicInteger ran = new AtomicInteger(0);
+        final CustomExecutor p = new CustomExecutor(poolSize);
+        final CountDownLatch threadsStarted = new CountDownLatch(poolSize);
+        Runnable waiter = new CheckedRunnable() { public void realRun() {
+            threadsStarted.countDown();
+            try {
+                MILLISECONDS.sleep(2 * LONG_DELAY_MS);
+            } catch (InterruptedException success) {}
+            ran.getAndIncrement();
+        }};
+        for (int i = 0; i < count; i++)
+            p.execute(waiter);
+        await(threadsStarted);
+        assertEquals(poolSize, p.getActiveCount());
+        assertEquals(0, p.getCompletedTaskCount());
+        final List<Runnable> queuedTasks;
+        try {
+            queuedTasks = p.shutdownNow();
+        } catch (SecurityException ok) {
+            return; // Allowed in case test doesn't have privs
+        }
+        assertTrue(p.isShutdown());
+        assertTrue(p.getQueue().isEmpty());
+        assertEquals(count - poolSize, queuedTasks.size());
+        assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+        assertEquals(poolSize, ran.get());
+        assertEquals(poolSize, p.getCompletedTaskCount());
+    }
+
+    /**
+     * shutdownNow returns a list containing tasks that were not run,
+     * and those tasks are drained from the queue
+     */
+    public void testShutdownNow_delayedTasks() throws InterruptedException {
+        final CustomExecutor p = new CustomExecutor(1);
+        List<ScheduledFuture> tasks = new ArrayList<>();
+        for (int i = 0; i < 3; i++) {
+            Runnable r = new NoOpRunnable();
+            tasks.add(p.schedule(r, 9, SECONDS));
+            tasks.add(p.scheduleAtFixedRate(r, 9, 9, SECONDS));
+            tasks.add(p.scheduleWithFixedDelay(r, 9, 9, SECONDS));
+        }
+        if (testImplementationDetails)
+            assertEquals(new HashSet(tasks), new HashSet(p.getQueue()));
+        final List<Runnable> queuedTasks;
+        try {
+            queuedTasks = p.shutdownNow();
+        } catch (SecurityException ok) {
+            return; // Allowed in case test doesn't have privs
+        }
+        assertTrue(p.isShutdown());
+        assertTrue(p.getQueue().isEmpty());
+        if (testImplementationDetails)
+            assertEquals(new HashSet(tasks), new HashSet(queuedTasks));
+        assertEquals(tasks.size(), queuedTasks.size());
+        for (ScheduledFuture task : tasks) {
+            assertFalse(((CustomTask)task).ran);
+            assertFalse(task.isDone());
+            assertFalse(task.isCancelled());
+        }
+        assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+    }
+
+    /**
+     * By default, periodic tasks are cancelled at shutdown.
+     * By default, delayed tasks keep running after shutdown.
+     * Check that changing the default values work:
+     * - setExecuteExistingDelayedTasksAfterShutdownPolicy
+     * - setContinueExistingPeriodicTasksAfterShutdownPolicy
+     */
+    public void testShutdown_cancellation() throws Exception {
+        Boolean[] allBooleans = { null, Boolean.FALSE, Boolean.TRUE };
+        for (Boolean policy : allBooleans)
+    {
+        final int poolSize = 2;
+        final CustomExecutor p = new CustomExecutor(poolSize);
+        final boolean effectiveDelayedPolicy = (policy != Boolean.FALSE);
+        final boolean effectivePeriodicPolicy = (policy == Boolean.TRUE);
+        final boolean effectiveRemovePolicy = (policy == Boolean.TRUE);
+        if (policy != null) {
+            p.setExecuteExistingDelayedTasksAfterShutdownPolicy(policy);
+            p.setContinueExistingPeriodicTasksAfterShutdownPolicy(policy);
+            p.setRemoveOnCancelPolicy(policy);
+        }
+        assertEquals(effectiveDelayedPolicy,
+                     p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
+        assertEquals(effectivePeriodicPolicy,
+                     p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
+        assertEquals(effectiveRemovePolicy,
+                     p.getRemoveOnCancelPolicy());
+        // Strategy: Wedge the pool with poolSize "blocker" threads
+        final AtomicInteger ran = new AtomicInteger(0);
+        final CountDownLatch poolBlocked = new CountDownLatch(poolSize);
+        final CountDownLatch unblock = new CountDownLatch(1);
+        final CountDownLatch periodicLatch1 = new CountDownLatch(2);
+        final CountDownLatch periodicLatch2 = new CountDownLatch(2);
+        Runnable task = new CheckedRunnable() { public void realRun()
+                                                    throws InterruptedException {
+            poolBlocked.countDown();
+            assertTrue(unblock.await(LONG_DELAY_MS, MILLISECONDS));
+            ran.getAndIncrement();
+        }};
+        List<Future<?>> blockers = new ArrayList<>();
+        List<Future<?>> periodics = new ArrayList<>();
+        List<Future<?>> delayeds = new ArrayList<>();
+        for (int i = 0; i < poolSize; i++)
+            blockers.add(p.submit(task));
+        assertTrue(poolBlocked.await(LONG_DELAY_MS, MILLISECONDS));
+
+        periodics.add(p.scheduleAtFixedRate(countDowner(periodicLatch1),
+                                            1, 1, MILLISECONDS));
+        periodics.add(p.scheduleWithFixedDelay(countDowner(periodicLatch2),
+                                               1, 1, MILLISECONDS));
+        delayeds.add(p.schedule(task, 1, MILLISECONDS));
+
+        assertTrue(p.getQueue().containsAll(periodics));
+        assertTrue(p.getQueue().containsAll(delayeds));
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        assertTrue(p.isShutdown());
+        assertFalse(p.isTerminated());
+        for (Future<?> periodic : periodics) {
+            assertTrue(effectivePeriodicPolicy ^ periodic.isCancelled());
+            assertTrue(effectivePeriodicPolicy ^ periodic.isDone());
+        }
+        for (Future<?> delayed : delayeds) {
+            assertTrue(effectiveDelayedPolicy ^ delayed.isCancelled());
+            assertTrue(effectiveDelayedPolicy ^ delayed.isDone());
+        }
+        if (testImplementationDetails) {
+            assertEquals(effectivePeriodicPolicy,
+                         p.getQueue().containsAll(periodics));
+            assertEquals(effectiveDelayedPolicy,
+                         p.getQueue().containsAll(delayeds));
+        }
+        // Release all pool threads
+        unblock.countDown();
+
+        for (Future<?> delayed : delayeds) {
+            if (effectiveDelayedPolicy) {
+                assertNull(delayed.get());
+            }
+        }
+        if (effectivePeriodicPolicy) {
+            assertTrue(periodicLatch1.await(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(periodicLatch2.await(LONG_DELAY_MS, MILLISECONDS));
+            for (Future<?> periodic : periodics) {
+                assertTrue(periodic.cancel(false));
+                assertTrue(periodic.isCancelled());
+                assertTrue(periodic.isDone());
+            }
+        }
+        assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+        assertEquals(2 + (effectiveDelayedPolicy ? 1 : 0), ran.get());
+    }}
+
+    /**
+     * completed submit of callable returns result
+     */
+    public void testSubmitCallable() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<String> future = e.submit(new StringTask());
+            String result = future.get();
+            assertSame(TEST_STRING, result);
+        }
+    }
+
+    /**
+     * completed submit of runnable returns successfully
+     */
+    public void testSubmitRunnable() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<?> future = e.submit(new NoOpRunnable());
+            future.get();
+            assertTrue(future.isDone());
+        }
+    }
+
+    /**
+     * completed submit of (runnable, result) returns result
+     */
+    public void testSubmitRunnable2() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<String> future = e.submit(new NoOpRunnable(), TEST_STRING);
+            String result = future.get();
+            assertSame(TEST_STRING, result);
+        }
+    }
+
+    /**
+     * invokeAny(null) throws NPE
+     */
+    public void testInvokeAny1() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * invokeAny(empty collection) throws IAE
+     */
+    public void testInvokeAny2() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(new ArrayList<Callable<String>>());
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * invokeAny(c) throws NPE if c has null elements
+     */
+    public void testInvokeAny3() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(latchAwaitingStringTask(latch));
+            l.add(null);
+            try {
+                e.invokeAny(l);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+            latch.countDown();
+        }
+    }
+
+    /**
+     * invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testInvokeAny4() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new NPETask());
+            try {
+                e.invokeAny(l);
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * invokeAny(c) returns result of some task
+     */
+    public void testInvokeAny5() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l);
+            assertSame(TEST_STRING, result);
+        }
+    }
+
+    /**
+     * invokeAll(null) throws NPE
+     */
+    public void testInvokeAll1() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAll(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * invokeAll(empty collection) returns empty collection
+     */
+    public void testInvokeAll2() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>());
+            assertTrue(r.isEmpty());
+        }
+    }
+
+    /**
+     * invokeAll(c) throws NPE if c has null elements
+     */
+    public void testInvokeAll3() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(null);
+            try {
+                e.invokeAll(l);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * get of invokeAll(c) throws exception on failed task
+     */
+    public void testInvokeAll4() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new NPETask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(1, futures.size());
+            try {
+                futures.get(0).get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * invokeAll(c) returns results of all completed tasks
+     */
+    public void testInvokeAll5() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        }
+    }
+
+    /**
+     * timed invokeAny(null) throws NPE
+     */
+    public void testTimedInvokeAny1() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(,,null) throws NPE
+     */
+    public void testTimedInvokeAnyNullTimeUnit() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            try {
+                e.invokeAny(l, MEDIUM_DELAY_MS, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(empty collection) throws IAE
+     */
+    public void testTimedInvokeAny2() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAny3() throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(latchAwaitingStringTask(latch));
+            l.add(null);
+            try {
+                e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+            latch.countDown();
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testTimedInvokeAny4() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            long startTime = System.nanoTime();
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new NPETask());
+            try {
+                e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) returns result of some task
+     */
+    public void testTimedInvokeAny5() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            long startTime = System.nanoTime();
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+            assertSame(TEST_STRING, result);
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * timed invokeAll(null) throws NPE
+     */
+    public void testTimedInvokeAll1() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAll(,,null) throws NPE
+     */
+    public void testTimedInvokeAllNullTimeUnit() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            try {
+                e.invokeAll(l, MEDIUM_DELAY_MS, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAll(empty collection) returns empty collection
+     */
+    public void testTimedInvokeAll2() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
+            assertTrue(r.isEmpty());
+        }
+    }
+
+    /**
+     * timed invokeAll(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAll3() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(null);
+            try {
+                e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * get of element of invokeAll(c) throws exception on failed task
+     */
+    public void testTimedInvokeAll4() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new NPETask());
+            List<Future<String>> futures =
+                e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            assertEquals(1, futures.size());
+            try {
+                futures.get(0).get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * timed invokeAll(c) returns results of all completed tasks
+     */
+    public void testTimedInvokeAll5() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures =
+                e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        }
+    }
+
+    /**
+     * timed invokeAll(c) cancels tasks not completed by timeout
+     */
+    public void testTimedInvokeAll6() throws Exception {
+        for (long timeout = timeoutMillis();;) {
+            final CountDownLatch done = new CountDownLatch(1);
+            final Callable<String> waiter = new CheckedCallable<String>() {
+                public String realCall() {
+                    try { done.await(LONG_DELAY_MS, MILLISECONDS); }
+                    catch (InterruptedException ok) {}
+                    return "1"; }};
+            final ExecutorService p = new CustomExecutor(2);
+            try (PoolCleaner cleaner = cleaner(p, done)) {
+                List<Callable<String>> tasks = new ArrayList<>();
+                tasks.add(new StringTask("0"));
+                tasks.add(waiter);
+                tasks.add(new StringTask("2"));
+                long startTime = System.nanoTime();
+                List<Future<String>> futures =
+                    p.invokeAll(tasks, timeout, MILLISECONDS);
+                assertEquals(tasks.size(), futures.size());
+                assertTrue(millisElapsedSince(startTime) >= timeout);
+                for (Future future : futures)
+                    assertTrue(future.isDone());
+                assertTrue(futures.get(1).isCancelled());
+                try {
+                    assertEquals("0", futures.get(0).get());
+                    assertEquals("2", futures.get(2).get());
+                    break;
+                } catch (CancellationException retryWithLongerTimeout) {
+                    timeout *= 2;
+                    if (timeout >= LONG_DELAY_MS / 2)
+                        fail("expected exactly one task to be cancelled");
+                }
+            }
+        }
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/ScheduledExecutorTest.java b/jdk/test/java/util/concurrent/tck/ScheduledExecutorTest.java
new file mode 100644
index 00000000000..797dee3a845
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/ScheduledExecutorTest.java
@@ -0,0 +1,1259 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ScheduledExecutorTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ScheduledExecutorTest.class);
+    }
+
+    /**
+     * execute successfully executes a runnable
+     */
+    public void testExecute() throws InterruptedException {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch done = new CountDownLatch(1);
+            final Runnable task = new CheckedRunnable() {
+                public void realRun() { done.countDown(); }};
+            p.execute(task);
+            assertTrue(done.await(LONG_DELAY_MS, MILLISECONDS));
+        }
+    }
+
+    /**
+     * delayed schedule of callable successfully executes after delay
+     */
+    public void testSchedule1() throws Exception {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final long startTime = System.nanoTime();
+            final CountDownLatch done = new CountDownLatch(1);
+            Callable task = new CheckedCallable<Boolean>() {
+                public Boolean realCall() {
+                    done.countDown();
+                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                    return Boolean.TRUE;
+                }};
+            Future f = p.schedule(task, timeoutMillis(), MILLISECONDS);
+            assertSame(Boolean.TRUE, f.get());
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            assertTrue(done.await(0L, MILLISECONDS));
+        }
+    }
+
+    /**
+     * delayed schedule of runnable successfully executes after delay
+     */
+    public void testSchedule3() throws Exception {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final long startTime = System.nanoTime();
+            final CountDownLatch done = new CountDownLatch(1);
+            Runnable task = new CheckedRunnable() {
+                public void realRun() {
+                    done.countDown();
+                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                }};
+            Future f = p.schedule(task, timeoutMillis(), MILLISECONDS);
+            await(done);
+            assertNull(f.get(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        }
+    }
+
+    /**
+     * scheduleAtFixedRate executes runnable after given initial delay
+     */
+    public void testSchedule4() throws Exception {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final long startTime = System.nanoTime();
+            final CountDownLatch done = new CountDownLatch(1);
+            Runnable task = new CheckedRunnable() {
+                public void realRun() {
+                    done.countDown();
+                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                }};
+            ScheduledFuture f =
+                p.scheduleAtFixedRate(task, timeoutMillis(),
+                                      LONG_DELAY_MS, MILLISECONDS);
+            await(done);
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            f.cancel(true);
+        }
+    }
+
+    /**
+     * scheduleWithFixedDelay executes runnable after given initial delay
+     */
+    public void testSchedule5() throws Exception {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final long startTime = System.nanoTime();
+            final CountDownLatch done = new CountDownLatch(1);
+            Runnable task = new CheckedRunnable() {
+                public void realRun() {
+                    done.countDown();
+                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                }};
+            ScheduledFuture f =
+                p.scheduleWithFixedDelay(task, timeoutMillis(),
+                                         LONG_DELAY_MS, MILLISECONDS);
+            await(done);
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            f.cancel(true);
+        }
+    }
+
+    static class RunnableCounter implements Runnable {
+        AtomicInteger count = new AtomicInteger(0);
+        public void run() { count.getAndIncrement(); }
+    }
+
+    /**
+     * scheduleAtFixedRate executes series of tasks at given rate
+     */
+    public void testFixedRateSequence() throws InterruptedException {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            for (int delay = 1; delay <= LONG_DELAY_MS; delay *= 3) {
+                long startTime = System.nanoTime();
+                int cycles = 10;
+                final CountDownLatch done = new CountDownLatch(cycles);
+                Runnable task = new CheckedRunnable() {
+                    public void realRun() { done.countDown(); }};
+                ScheduledFuture h =
+                    p.scheduleAtFixedRate(task, 0, delay, MILLISECONDS);
+                await(done);
+                h.cancel(true);
+                double normalizedTime =
+                    (double) millisElapsedSince(startTime) / delay;
+                if (normalizedTime >= cycles - 1 &&
+                    normalizedTime <= cycles)
+                    return;
+            }
+            fail("unexpected execution rate");
+        }
+    }
+
+    /**
+     * scheduleWithFixedDelay executes series of tasks with given period
+     */
+    public void testFixedDelaySequence() throws InterruptedException {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            for (int delay = 1; delay <= LONG_DELAY_MS; delay *= 3) {
+                long startTime = System.nanoTime();
+                int cycles = 10;
+                final CountDownLatch done = new CountDownLatch(cycles);
+                Runnable task = new CheckedRunnable() {
+                    public void realRun() { done.countDown(); }};
+                ScheduledFuture h =
+                    p.scheduleWithFixedDelay(task, 0, delay, MILLISECONDS);
+                await(done);
+                h.cancel(true);
+                double normalizedTime =
+                    (double) millisElapsedSince(startTime) / delay;
+                if (normalizedTime >= cycles - 1 &&
+                    normalizedTime <= cycles)
+                    return;
+            }
+            fail("unexpected execution rate");
+        }
+    }
+
+    /**
+     * execute(null) throws NPE
+     */
+    public void testExecuteNull() throws InterruptedException {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.execute(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * schedule(null) throws NPE
+     */
+    public void testScheduleNull() throws InterruptedException {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                TrackedCallable callable = null;
+                Future f = p.schedule(callable, SHORT_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * execute throws RejectedExecutionException if shutdown
+     */
+    public void testSchedule1_RejectedExecutionException() throws InterruptedException {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.shutdown();
+                p.schedule(new NoOpRunnable(),
+                           MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (RejectedExecutionException success) {
+            } catch (SecurityException ok) {}
+        }
+    }
+
+    /**
+     * schedule throws RejectedExecutionException if shutdown
+     */
+    public void testSchedule2_RejectedExecutionException() throws InterruptedException {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.shutdown();
+                p.schedule(new NoOpCallable(),
+                           MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (RejectedExecutionException success) {
+            } catch (SecurityException ok) {}
+        }
+    }
+
+    /**
+     * schedule callable throws RejectedExecutionException if shutdown
+     */
+    public void testSchedule3_RejectedExecutionException() throws InterruptedException {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.shutdown();
+                p.schedule(new NoOpCallable(),
+                           MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (RejectedExecutionException success) {
+            } catch (SecurityException ok) {}
+        }
+    }
+
+    /**
+     * scheduleAtFixedRate throws RejectedExecutionException if shutdown
+     */
+    public void testScheduleAtFixedRate1_RejectedExecutionException() throws InterruptedException {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.shutdown();
+                p.scheduleAtFixedRate(new NoOpRunnable(),
+                                      MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (RejectedExecutionException success) {
+            } catch (SecurityException ok) {}
+        }
+    }
+
+    /**
+     * scheduleWithFixedDelay throws RejectedExecutionException if shutdown
+     */
+    public void testScheduleWithFixedDelay1_RejectedExecutionException() throws InterruptedException {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.shutdown();
+                p.scheduleWithFixedDelay(new NoOpRunnable(),
+                                         MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (RejectedExecutionException success) {
+            } catch (SecurityException ok) {}
+        }
+    }
+
+    /**
+     * getActiveCount increases but doesn't overestimate, when a
+     * thread becomes active
+     */
+    public void testGetActiveCount() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            assertEquals(0, p.getActiveCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getActiveCount());
+                    await(done);
+                }});
+            await(threadStarted);
+            assertEquals(1, p.getActiveCount());
+        }
+    }
+
+    /**
+     * getCompletedTaskCount increases, but doesn't overestimate,
+     * when tasks complete
+     */
+    public void testGetCompletedTaskCount() throws InterruptedException {
+        final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            final CountDownLatch threadProceed = new CountDownLatch(1);
+            final CountDownLatch threadDone = new CountDownLatch(1);
+            assertEquals(0, p.getCompletedTaskCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(0, p.getCompletedTaskCount());
+                    threadProceed.await();
+                    threadDone.countDown();
+                }});
+            await(threadStarted);
+            assertEquals(0, p.getCompletedTaskCount());
+            threadProceed.countDown();
+            threadDone.await();
+            long startTime = System.nanoTime();
+            while (p.getCompletedTaskCount() != 1) {
+                if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                    fail("timed out");
+                Thread.yield();
+            }
+        }
+    }
+
+    /**
+     * getCorePoolSize returns size given in constructor if not otherwise set
+     */
+    public void testGetCorePoolSize() throws InterruptedException {
+        ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertEquals(1, p.getCorePoolSize());
+        }
+    }
+
+    /**
+     * getLargestPoolSize increases, but doesn't overestimate, when
+     * multiple threads active
+     */
+    public void testGetLargestPoolSize() throws InterruptedException {
+        final int THREADS = 3;
+        final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(THREADS);
+        final CountDownLatch threadsStarted = new CountDownLatch(THREADS);
+        final CountDownLatch done = new CountDownLatch(1);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            assertEquals(0, p.getLargestPoolSize());
+            for (int i = 0; i < THREADS; i++)
+                p.execute(new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadsStarted.countDown();
+                        await(done);
+                        assertEquals(THREADS, p.getLargestPoolSize());
+                    }});
+            await(threadsStarted);
+            assertEquals(THREADS, p.getLargestPoolSize());
+        }
+        assertEquals(THREADS, p.getLargestPoolSize());
+    }
+
+    /**
+     * getPoolSize increases, but doesn't overestimate, when threads
+     * become active
+     */
+    public void testGetPoolSize() throws InterruptedException {
+        final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            assertEquals(0, p.getPoolSize());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getPoolSize());
+                    await(done);
+                }});
+            await(threadStarted);
+            assertEquals(1, p.getPoolSize());
+        }
+    }
+
+    /**
+     * getTaskCount increases, but doesn't overestimate, when tasks
+     * submitted
+     */
+    public void testGetTaskCount() throws InterruptedException {
+        final int TASKS = 3;
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            assertEquals(0, p.getTaskCount());
+            assertEquals(0, p.getCompletedTaskCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    await(done);
+                }});
+            await(threadStarted);
+            assertEquals(1, p.getTaskCount());
+            assertEquals(0, p.getCompletedTaskCount());
+            for (int i = 0; i < TASKS; i++) {
+                assertEquals(1 + i, p.getTaskCount());
+                p.execute(new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadStarted.countDown();
+                        assertEquals(1 + TASKS, p.getTaskCount());
+                        await(done);
+                    }});
+            }
+            assertEquals(1 + TASKS, p.getTaskCount());
+            assertEquals(0, p.getCompletedTaskCount());
+        }
+        assertEquals(1 + TASKS, p.getTaskCount());
+        assertEquals(1 + TASKS, p.getCompletedTaskCount());
+    }
+
+    /**
+     * getThreadFactory returns factory in constructor if not set
+     */
+    public void testGetThreadFactory() throws InterruptedException {
+        final ThreadFactory threadFactory = new SimpleThreadFactory();
+        final ScheduledThreadPoolExecutor p =
+            new ScheduledThreadPoolExecutor(1, threadFactory);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertSame(threadFactory, p.getThreadFactory());
+        }
+    }
+
+    /**
+     * setThreadFactory sets the thread factory returned by getThreadFactory
+     */
+    public void testSetThreadFactory() throws InterruptedException {
+        ThreadFactory threadFactory = new SimpleThreadFactory();
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            p.setThreadFactory(threadFactory);
+            assertSame(threadFactory, p.getThreadFactory());
+        }
+    }
+
+    /**
+     * setThreadFactory(null) throws NPE
+     */
+    public void testSetThreadFactoryNull() throws InterruptedException {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.setThreadFactory(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * isShutdown is false before shutdown, true after
+     */
+    public void testIsShutdown() {
+
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try {
+            assertFalse(p.isShutdown());
+        }
+        finally {
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+        }
+        assertTrue(p.isShutdown());
+    }
+
+    /**
+     * isTerminated is false before termination, true after
+     */
+    public void testIsTerminated() throws InterruptedException {
+        final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            final CountDownLatch done = new CountDownLatch(1);
+            assertFalse(p.isTerminated());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(p.isTerminated());
+                    threadStarted.countDown();
+                    await(done);
+                }});
+            await(threadStarted);
+            assertFalse(p.isTerminating());
+            done.countDown();
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(p.isTerminated());
+        }
+    }
+
+    /**
+     * isTerminating is not true when running or when terminated
+     */
+    public void testIsTerminating() throws InterruptedException {
+        final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertFalse(p.isTerminating());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(p.isTerminating());
+                    threadStarted.countDown();
+                    await(done);
+                }});
+            await(threadStarted);
+            assertFalse(p.isTerminating());
+            done.countDown();
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(p.isTerminated());
+            assertFalse(p.isTerminating());
+        }
+    }
+
+    /**
+     * getQueue returns the work queue, which contains queued tasks
+     */
+    public void testGetQueue() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            ScheduledFuture[] tasks = new ScheduledFuture[5];
+            for (int i = 0; i < tasks.length; i++) {
+                Runnable r = new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadStarted.countDown();
+                        await(done);
+                    }};
+                tasks[i] = p.schedule(r, 1, MILLISECONDS);
+            }
+            await(threadStarted);
+            BlockingQueue<Runnable> q = p.getQueue();
+            assertTrue(q.contains(tasks[tasks.length - 1]));
+            assertFalse(q.contains(tasks[0]));
+        }
+    }
+
+    /**
+     * remove(task) removes queued task, and fails to remove active task
+     */
+    public void testRemove() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            ScheduledFuture[] tasks = new ScheduledFuture[5];
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            for (int i = 0; i < tasks.length; i++) {
+                Runnable r = new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadStarted.countDown();
+                        await(done);
+                    }};
+                tasks[i] = p.schedule(r, 1, MILLISECONDS);
+            }
+            await(threadStarted);
+            BlockingQueue<Runnable> q = p.getQueue();
+            assertFalse(p.remove((Runnable)tasks[0]));
+            assertTrue(q.contains((Runnable)tasks[4]));
+            assertTrue(q.contains((Runnable)tasks[3]));
+            assertTrue(p.remove((Runnable)tasks[4]));
+            assertFalse(p.remove((Runnable)tasks[4]));
+            assertFalse(q.contains((Runnable)tasks[4]));
+            assertTrue(q.contains((Runnable)tasks[3]));
+            assertTrue(p.remove((Runnable)tasks[3]));
+            assertFalse(q.contains((Runnable)tasks[3]));
+        }
+    }
+
+    /**
+     * purge eventually removes cancelled tasks from the queue
+     */
+    public void testPurge() throws InterruptedException {
+        final ScheduledFuture[] tasks = new ScheduledFuture[5];
+        final Runnable releaser = new Runnable() { public void run() {
+            for (ScheduledFuture task : tasks)
+                if (task != null) task.cancel(true); }};
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p, releaser)) {
+            for (int i = 0; i < tasks.length; i++)
+                tasks[i] = p.schedule(new SmallPossiblyInterruptedRunnable(),
+                                      LONG_DELAY_MS, MILLISECONDS);
+            int max = tasks.length;
+            if (tasks[4].cancel(true)) --max;
+            if (tasks[3].cancel(true)) --max;
+            // There must eventually be an interference-free point at
+            // which purge will not fail. (At worst, when queue is empty.)
+            long startTime = System.nanoTime();
+            do {
+                p.purge();
+                long count = p.getTaskCount();
+                if (count == max)
+                    return;
+            } while (millisElapsedSince(startTime) < LONG_DELAY_MS);
+            fail("Purge failed to remove cancelled tasks");
+        }
+    }
+
+    /**
+     * shutdownNow returns a list containing tasks that were not run,
+     * and those tasks are drained from the queue
+     */
+    public void testShutdownNow() throws InterruptedException {
+        final int poolSize = 2;
+        final int count = 5;
+        final AtomicInteger ran = new AtomicInteger(0);
+        final ScheduledThreadPoolExecutor p =
+            new ScheduledThreadPoolExecutor(poolSize);
+        final CountDownLatch threadsStarted = new CountDownLatch(poolSize);
+        Runnable waiter = new CheckedRunnable() { public void realRun() {
+            threadsStarted.countDown();
+            try {
+                MILLISECONDS.sleep(2 * LONG_DELAY_MS);
+            } catch (InterruptedException success) {}
+            ran.getAndIncrement();
+        }};
+        for (int i = 0; i < count; i++)
+            p.execute(waiter);
+        await(threadsStarted);
+        assertEquals(poolSize, p.getActiveCount());
+        assertEquals(0, p.getCompletedTaskCount());
+        final List<Runnable> queuedTasks;
+        try {
+            queuedTasks = p.shutdownNow();
+        } catch (SecurityException ok) {
+            return; // Allowed in case test doesn't have privs
+        }
+        assertTrue(p.isShutdown());
+        assertTrue(p.getQueue().isEmpty());
+        assertEquals(count - poolSize, queuedTasks.size());
+        assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+        assertEquals(poolSize, ran.get());
+        assertEquals(poolSize, p.getCompletedTaskCount());
+    }
+
+    /**
+     * shutdownNow returns a list containing tasks that were not run,
+     * and those tasks are drained from the queue
+     */
+    public void testShutdownNow_delayedTasks() throws InterruptedException {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        List<ScheduledFuture> tasks = new ArrayList<>();
+        for (int i = 0; i < 3; i++) {
+            Runnable r = new NoOpRunnable();
+            tasks.add(p.schedule(r, 9, SECONDS));
+            tasks.add(p.scheduleAtFixedRate(r, 9, 9, SECONDS));
+            tasks.add(p.scheduleWithFixedDelay(r, 9, 9, SECONDS));
+        }
+        if (testImplementationDetails)
+            assertEquals(new HashSet(tasks), new HashSet(p.getQueue()));
+        final List<Runnable> queuedTasks;
+        try {
+            queuedTasks = p.shutdownNow();
+        } catch (SecurityException ok) {
+            return; // Allowed in case test doesn't have privs
+        }
+        assertTrue(p.isShutdown());
+        assertTrue(p.getQueue().isEmpty());
+        if (testImplementationDetails)
+            assertEquals(new HashSet(tasks), new HashSet(queuedTasks));
+        assertEquals(tasks.size(), queuedTasks.size());
+        for (ScheduledFuture task : tasks) {
+            assertFalse(task.isDone());
+            assertFalse(task.isCancelled());
+        }
+        assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+    }
+
+    /**
+     * By default, periodic tasks are cancelled at shutdown.
+     * By default, delayed tasks keep running after shutdown.
+     * Check that changing the default values work:
+     * - setExecuteExistingDelayedTasksAfterShutdownPolicy
+     * - setContinueExistingPeriodicTasksAfterShutdownPolicy
+     */
+    public void testShutdown_cancellation() throws Exception {
+        Boolean[] allBooleans = { null, Boolean.FALSE, Boolean.TRUE };
+        for (Boolean policy : allBooleans)
+    {
+        final int poolSize = 2;
+        final ScheduledThreadPoolExecutor p
+            = new ScheduledThreadPoolExecutor(poolSize);
+        final boolean effectiveDelayedPolicy = (policy != Boolean.FALSE);
+        final boolean effectivePeriodicPolicy = (policy == Boolean.TRUE);
+        final boolean effectiveRemovePolicy = (policy == Boolean.TRUE);
+        if (policy != null) {
+            p.setExecuteExistingDelayedTasksAfterShutdownPolicy(policy);
+            p.setContinueExistingPeriodicTasksAfterShutdownPolicy(policy);
+            p.setRemoveOnCancelPolicy(policy);
+        }
+        assertEquals(effectiveDelayedPolicy,
+                     p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
+        assertEquals(effectivePeriodicPolicy,
+                     p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
+        assertEquals(effectiveRemovePolicy,
+                     p.getRemoveOnCancelPolicy());
+        // Strategy: Wedge the pool with poolSize "blocker" threads
+        final AtomicInteger ran = new AtomicInteger(0);
+        final CountDownLatch poolBlocked = new CountDownLatch(poolSize);
+        final CountDownLatch unblock = new CountDownLatch(1);
+        final CountDownLatch periodicLatch1 = new CountDownLatch(2);
+        final CountDownLatch periodicLatch2 = new CountDownLatch(2);
+        Runnable task = new CheckedRunnable() { public void realRun()
+                                                    throws InterruptedException {
+            poolBlocked.countDown();
+            assertTrue(unblock.await(LONG_DELAY_MS, MILLISECONDS));
+            ran.getAndIncrement();
+        }};
+        List<Future<?>> blockers = new ArrayList<>();
+        List<Future<?>> periodics = new ArrayList<>();
+        List<Future<?>> delayeds = new ArrayList<>();
+        for (int i = 0; i < poolSize; i++)
+            blockers.add(p.submit(task));
+        assertTrue(poolBlocked.await(LONG_DELAY_MS, MILLISECONDS));
+
+        periodics.add(p.scheduleAtFixedRate(countDowner(periodicLatch1),
+                                            1, 1, MILLISECONDS));
+        periodics.add(p.scheduleWithFixedDelay(countDowner(periodicLatch2),
+                                               1, 1, MILLISECONDS));
+        delayeds.add(p.schedule(task, 1, MILLISECONDS));
+
+        assertTrue(p.getQueue().containsAll(periodics));
+        assertTrue(p.getQueue().containsAll(delayeds));
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        assertTrue(p.isShutdown());
+        assertFalse(p.isTerminated());
+        for (Future<?> periodic : periodics) {
+            assertTrue(effectivePeriodicPolicy ^ periodic.isCancelled());
+            assertTrue(effectivePeriodicPolicy ^ periodic.isDone());
+        }
+        for (Future<?> delayed : delayeds) {
+            assertTrue(effectiveDelayedPolicy ^ delayed.isCancelled());
+            assertTrue(effectiveDelayedPolicy ^ delayed.isDone());
+        }
+        if (testImplementationDetails) {
+            assertEquals(effectivePeriodicPolicy,
+                         p.getQueue().containsAll(periodics));
+            assertEquals(effectiveDelayedPolicy,
+                         p.getQueue().containsAll(delayeds));
+        }
+        // Release all pool threads
+        unblock.countDown();
+
+        for (Future<?> delayed : delayeds) {
+            if (effectiveDelayedPolicy) {
+                assertNull(delayed.get());
+            }
+        }
+        if (effectivePeriodicPolicy) {
+            assertTrue(periodicLatch1.await(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(periodicLatch2.await(LONG_DELAY_MS, MILLISECONDS));
+            for (Future<?> periodic : periodics) {
+                assertTrue(periodic.cancel(false));
+                assertTrue(periodic.isCancelled());
+                assertTrue(periodic.isDone());
+            }
+        }
+        assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+        assertEquals(2 + (effectiveDelayedPolicy ? 1 : 0), ran.get());
+    }}
+
+    /**
+     * completed submit of callable returns result
+     */
+    public void testSubmitCallable() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<String> future = e.submit(new StringTask());
+            String result = future.get();
+            assertSame(TEST_STRING, result);
+        }
+    }
+
+    /**
+     * completed submit of runnable returns successfully
+     */
+    public void testSubmitRunnable() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<?> future = e.submit(new NoOpRunnable());
+            future.get();
+            assertTrue(future.isDone());
+        }
+    }
+
+    /**
+     * completed submit of (runnable, result) returns result
+     */
+    public void testSubmitRunnable2() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<String> future = e.submit(new NoOpRunnable(), TEST_STRING);
+            String result = future.get();
+            assertSame(TEST_STRING, result);
+        }
+    }
+
+    /**
+     * invokeAny(null) throws NPE
+     */
+    public void testInvokeAny1() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * invokeAny(empty collection) throws IAE
+     */
+    public void testInvokeAny2() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(new ArrayList<Callable<String>>());
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * invokeAny(c) throws NPE if c has null elements
+     */
+    public void testInvokeAny3() throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(latchAwaitingStringTask(latch));
+            l.add(null);
+            try {
+                e.invokeAny(l);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+            latch.countDown();
+        }
+    }
+
+    /**
+     * invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testInvokeAny4() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new NPETask());
+            try {
+                e.invokeAny(l);
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * invokeAny(c) returns result of some task
+     */
+    public void testInvokeAny5() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l);
+            assertSame(TEST_STRING, result);
+        }
+    }
+
+    /**
+     * invokeAll(null) throws NPE
+     */
+    public void testInvokeAll1() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAll(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * invokeAll(empty collection) returns empty collection
+     */
+    public void testInvokeAll2() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>());
+            assertTrue(r.isEmpty());
+        }
+    }
+
+    /**
+     * invokeAll(c) throws NPE if c has null elements
+     */
+    public void testInvokeAll3() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(null);
+            try {
+                e.invokeAll(l);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * get of invokeAll(c) throws exception on failed task
+     */
+    public void testInvokeAll4() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new NPETask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(1, futures.size());
+            try {
+                futures.get(0).get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * invokeAll(c) returns results of all completed tasks
+     */
+    public void testInvokeAll5() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        }
+    }
+
+    /**
+     * timed invokeAny(null) throws NPE
+     */
+    public void testTimedInvokeAny1() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(,,null) throws NPE
+     */
+    public void testTimedInvokeAnyNullTimeUnit() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            try {
+                e.invokeAny(l, MEDIUM_DELAY_MS, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(empty collection) throws IAE
+     */
+    public void testTimedInvokeAny2() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAny3() throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(latchAwaitingStringTask(latch));
+            l.add(null);
+            try {
+                e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+            latch.countDown();
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testTimedInvokeAny4() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            long startTime = System.nanoTime();
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new NPETask());
+            try {
+                e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) returns result of some task
+     */
+    public void testTimedInvokeAny5() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            long startTime = System.nanoTime();
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+            assertSame(TEST_STRING, result);
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * timed invokeAll(null) throws NPE
+     */
+    public void testTimedInvokeAll1() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAll(,,null) throws NPE
+     */
+    public void testTimedInvokeAllNullTimeUnit() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            try {
+                e.invokeAll(l, MEDIUM_DELAY_MS, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAll(empty collection) returns empty collection
+     */
+    public void testTimedInvokeAll2() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>(),
+                                                 MEDIUM_DELAY_MS, MILLISECONDS);
+            assertTrue(r.isEmpty());
+        }
+    }
+
+    /**
+     * timed invokeAll(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAll3() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(null);
+            try {
+                e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * get of element of invokeAll(c) throws exception on failed task
+     */
+    public void testTimedInvokeAll4() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new NPETask());
+            List<Future<String>> futures =
+                e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
+            assertEquals(1, futures.size());
+            try {
+                futures.get(0).get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * timed invokeAll(c) returns results of all completed tasks
+     */
+    public void testTimedInvokeAll5() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures =
+                e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        }
+    }
+
+    /**
+     * timed invokeAll(c) cancels tasks not completed by timeout
+     */
+    public void testTimedInvokeAll6() throws Exception {
+        for (long timeout = timeoutMillis();;) {
+            final CountDownLatch done = new CountDownLatch(1);
+            final Callable<String> waiter = new CheckedCallable<String>() {
+                public String realCall() {
+                    try { done.await(LONG_DELAY_MS, MILLISECONDS); }
+                    catch (InterruptedException ok) {}
+                    return "1"; }};
+            final ExecutorService p = new ScheduledThreadPoolExecutor(2);
+            try (PoolCleaner cleaner = cleaner(p, done)) {
+                List<Callable<String>> tasks = new ArrayList<>();
+                tasks.add(new StringTask("0"));
+                tasks.add(waiter);
+                tasks.add(new StringTask("2"));
+                long startTime = System.nanoTime();
+                List<Future<String>> futures =
+                    p.invokeAll(tasks, timeout, MILLISECONDS);
+                assertEquals(tasks.size(), futures.size());
+                assertTrue(millisElapsedSince(startTime) >= timeout);
+                for (Future future : futures)
+                    assertTrue(future.isDone());
+                assertTrue(futures.get(1).isCancelled());
+                try {
+                    assertEquals("0", futures.get(0).get());
+                    assertEquals("2", futures.get(2).get());
+                    break;
+                } catch (CancellationException retryWithLongerTimeout) {
+                    timeout *= 2;
+                    if (timeout >= LONG_DELAY_MS / 2)
+                        fail("expected exactly one task to be cancelled");
+                }
+            }
+        }
+    }
+
+    /**
+     * A fixed delay task with overflowing period should not prevent a
+     * one-shot task from executing.
+     * https://bugs.openjdk.java.net/browse/JDK-8051859
+     */
+    public void testScheduleWithFixedDelay_overflow() throws Exception {
+        final CountDownLatch delayedDone = new CountDownLatch(1);
+        final CountDownLatch immediateDone = new CountDownLatch(1);
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final Runnable immediate = new Runnable() { public void run() {
+                immediateDone.countDown();
+            }};
+            final Runnable delayed = new Runnable() { public void run() {
+                delayedDone.countDown();
+                p.submit(immediate);
+            }};
+            p.scheduleWithFixedDelay(delayed, 0L, Long.MAX_VALUE, SECONDS);
+            await(delayedDone);
+            await(immediateDone);
+        }
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/SemaphoreTest.java b/jdk/test/java/util/concurrent/tck/SemaphoreTest.java
new file mode 100644
index 00000000000..0e85d7c73fc
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/SemaphoreTest.java
@@ -0,0 +1,669 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.Collection;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Semaphore;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class SemaphoreTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(SemaphoreTest.class);
+    }
+
+    /**
+     * Subclass to expose protected methods
+     */
+    static class PublicSemaphore extends Semaphore {
+        PublicSemaphore(int permits) { super(permits); }
+        PublicSemaphore(int permits, boolean fair) { super(permits, fair); }
+        public Collection<Thread> getQueuedThreads() {
+            return super.getQueuedThreads();
+        }
+        public boolean hasQueuedThread(Thread t) {
+            return super.getQueuedThreads().contains(t);
+        }
+        public void reducePermits(int reduction) {
+            super.reducePermits(reduction);
+        }
+    }
+
+    /**
+     * A runnable calling acquire
+     */
+    class InterruptibleLockRunnable extends CheckedRunnable {
+        final Semaphore lock;
+        InterruptibleLockRunnable(Semaphore s) { lock = s; }
+        public void realRun() {
+            try {
+                lock.acquire();
+            }
+            catch (InterruptedException ignored) {}
+        }
+    }
+
+    /**
+     * A runnable calling acquire that expects to be interrupted
+     */
+    class InterruptedLockRunnable extends CheckedInterruptedRunnable {
+        final Semaphore lock;
+        InterruptedLockRunnable(Semaphore s) { lock = s; }
+        public void realRun() throws InterruptedException {
+            lock.acquire();
+        }
+    }
+
+    /**
+     * Spin-waits until s.hasQueuedThread(t) becomes true.
+     */
+    void waitForQueuedThread(PublicSemaphore s, Thread t) {
+        long startTime = System.nanoTime();
+        while (!s.hasQueuedThread(t)) {
+            if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                throw new AssertionFailedError("timed out");
+            Thread.yield();
+        }
+        assertTrue(s.hasQueuedThreads());
+        assertTrue(t.isAlive());
+    }
+
+    /**
+     * Spin-waits until s.hasQueuedThreads() becomes true.
+     */
+    void waitForQueuedThreads(Semaphore s) {
+        long startTime = System.nanoTime();
+        while (!s.hasQueuedThreads()) {
+            if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                throw new AssertionFailedError("timed out");
+            Thread.yield();
+        }
+    }
+
+    enum AcquireMethod {
+        acquire() {
+            void acquire(Semaphore s) throws InterruptedException {
+                s.acquire();
+            }
+        },
+        acquireN() {
+            void acquire(Semaphore s, int permits) throws InterruptedException {
+                s.acquire(permits);
+            }
+        },
+        acquireUninterruptibly() {
+            void acquire(Semaphore s) {
+                s.acquireUninterruptibly();
+            }
+        },
+        acquireUninterruptiblyN() {
+            void acquire(Semaphore s, int permits) {
+                s.acquireUninterruptibly(permits);
+            }
+        },
+        tryAcquire() {
+            void acquire(Semaphore s) {
+                assertTrue(s.tryAcquire());
+            }
+        },
+        tryAcquireN() {
+            void acquire(Semaphore s, int permits) {
+                assertTrue(s.tryAcquire(permits));
+            }
+        },
+        tryAcquireTimed() {
+            void acquire(Semaphore s) throws InterruptedException {
+                assertTrue(s.tryAcquire(2 * LONG_DELAY_MS, MILLISECONDS));
+            }
+        },
+        tryAcquireTimedN {
+            void acquire(Semaphore s, int permits) throws InterruptedException {
+                assertTrue(s.tryAcquire(permits, 2 * LONG_DELAY_MS, MILLISECONDS));
+            }
+        };
+
+        // Intentionally meta-circular
+
+        /** Acquires 1 permit. */
+        void acquire(Semaphore s) throws InterruptedException {
+            acquire(s, 1);
+        }
+        /** Acquires the given number of permits. */
+        void acquire(Semaphore s, int permits) throws InterruptedException {
+            for (int i = 0; i < permits; i++)
+                acquire(s);
+        }
+    }
+
+    /**
+     * Zero, negative, and positive initial values are allowed in constructor
+     */
+    public void testConstructor()      { testConstructor(false); }
+    public void testConstructor_fair() { testConstructor(true); }
+    public void testConstructor(boolean fair) {
+        for (int permits : new int[] { -42, -1, 0, 1, 42 }) {
+            Semaphore s = new Semaphore(permits, fair);
+            assertEquals(permits, s.availablePermits());
+            assertEquals(fair, s.isFair());
+        }
+    }
+
+    /**
+     * Constructor without fairness argument behaves as nonfair
+     */
+    public void testConstructorDefaultsToNonFair() {
+        for (int permits : new int[] { -42, -1, 0, 1, 42 }) {
+            Semaphore s = new Semaphore(permits);
+            assertEquals(permits, s.availablePermits());
+            assertFalse(s.isFair());
+        }
+    }
+
+    /**
+     * tryAcquire succeeds when sufficient permits, else fails
+     */
+    public void testTryAcquireInSameThread()      { testTryAcquireInSameThread(false); }
+    public void testTryAcquireInSameThread_fair() { testTryAcquireInSameThread(true); }
+    public void testTryAcquireInSameThread(boolean fair) {
+        Semaphore s = new Semaphore(2, fair);
+        assertEquals(2, s.availablePermits());
+        assertTrue(s.tryAcquire());
+        assertTrue(s.tryAcquire());
+        assertEquals(0, s.availablePermits());
+        assertFalse(s.tryAcquire());
+        assertFalse(s.tryAcquire());
+        assertEquals(0, s.availablePermits());
+    }
+
+    /**
+     * timed tryAcquire times out
+     */
+    public void testTryAcquire_timeout()      { testTryAcquire_timeout(false); }
+    public void testTryAcquire_timeout_fair() { testTryAcquire_timeout(true); }
+    public void testTryAcquire_timeout(boolean fair) {
+        Semaphore s = new Semaphore(0, fair);
+        long startTime = System.nanoTime();
+        try { assertFalse(s.tryAcquire(timeoutMillis(), MILLISECONDS)); }
+        catch (InterruptedException e) { threadUnexpectedException(e); }
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+    }
+
+    /**
+     * timed tryAcquire(N) times out
+     */
+    public void testTryAcquireN_timeout()      { testTryAcquireN_timeout(false); }
+    public void testTryAcquireN_timeout_fair() { testTryAcquireN_timeout(true); }
+    public void testTryAcquireN_timeout(boolean fair) {
+        Semaphore s = new Semaphore(2, fair);
+        long startTime = System.nanoTime();
+        try { assertFalse(s.tryAcquire(3, timeoutMillis(), MILLISECONDS)); }
+        catch (InterruptedException e) { threadUnexpectedException(e); }
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+    }
+
+    /**
+     * acquire(), acquire(N), timed tryAcquired, timed tryAcquire(N)
+     * are interruptible
+     */
+    public void testInterruptible_acquire()               { testInterruptible(false, AcquireMethod.acquire); }
+    public void testInterruptible_acquire_fair()          { testInterruptible(true,  AcquireMethod.acquire); }
+    public void testInterruptible_acquireN()              { testInterruptible(false, AcquireMethod.acquireN); }
+    public void testInterruptible_acquireN_fair()         { testInterruptible(true,  AcquireMethod.acquireN); }
+    public void testInterruptible_tryAcquireTimed()       { testInterruptible(false, AcquireMethod.tryAcquireTimed); }
+    public void testInterruptible_tryAcquireTimed_fair()  { testInterruptible(true,  AcquireMethod.tryAcquireTimed); }
+    public void testInterruptible_tryAcquireTimedN()      { testInterruptible(false, AcquireMethod.tryAcquireTimedN); }
+    public void testInterruptible_tryAcquireTimedN_fair() { testInterruptible(true,  AcquireMethod.tryAcquireTimedN); }
+    public void testInterruptible(boolean fair, final AcquireMethod acquirer) {
+        final PublicSemaphore s = new PublicSemaphore(0, fair);
+        final Semaphore pleaseInterrupt = new Semaphore(0, fair);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                // Interrupt before acquire
+                Thread.currentThread().interrupt();
+                try {
+                    acquirer.acquire(s);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+
+                // Interrupt during acquire
+                try {
+                    acquirer.acquire(s);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+
+                // Interrupt before acquire(N)
+                Thread.currentThread().interrupt();
+                try {
+                    acquirer.acquire(s, 3);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+
+                pleaseInterrupt.release();
+
+                // Interrupt during acquire(N)
+                try {
+                    acquirer.acquire(s, 3);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+            }});
+
+        waitForQueuedThread(s, t);
+        t.interrupt();
+        await(pleaseInterrupt);
+        waitForQueuedThread(s, t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * acquireUninterruptibly(), acquireUninterruptibly(N) are
+     * uninterruptible
+     */
+    public void testUninterruptible_acquireUninterruptibly()       { testUninterruptible(false, AcquireMethod.acquireUninterruptibly); }
+    public void testUninterruptible_acquireUninterruptibly_fair()  { testUninterruptible(true,  AcquireMethod.acquireUninterruptibly); }
+    public void testUninterruptible_acquireUninterruptiblyN()      { testUninterruptible(false, AcquireMethod.acquireUninterruptiblyN); }
+    public void testUninterruptible_acquireUninterruptiblyN_fair() { testUninterruptible(true,  AcquireMethod.acquireUninterruptiblyN); }
+    public void testUninterruptible(boolean fair, final AcquireMethod acquirer) {
+        final PublicSemaphore s = new PublicSemaphore(0, fair);
+        final Semaphore pleaseInterrupt = new Semaphore(-1, fair);
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                // Interrupt before acquire
+                pleaseInterrupt.release();
+                Thread.currentThread().interrupt();
+                acquirer.acquire(s);
+                assertTrue(Thread.interrupted());
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                // Interrupt during acquire
+                pleaseInterrupt.release();
+                acquirer.acquire(s);
+                assertTrue(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        waitForQueuedThread(s, t1);
+        waitForQueuedThread(s, t2);
+        t2.interrupt();
+
+        assertThreadStaysAlive(t1);
+        assertTrue(t2.isAlive());
+
+        s.release(2);
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * hasQueuedThreads reports whether there are waiting threads
+     */
+    public void testHasQueuedThreads()      { testHasQueuedThreads(false); }
+    public void testHasQueuedThreads_fair() { testHasQueuedThreads(true); }
+    public void testHasQueuedThreads(boolean fair) {
+        final PublicSemaphore lock = new PublicSemaphore(1, fair);
+        assertFalse(lock.hasQueuedThreads());
+        lock.acquireUninterruptibly();
+        Thread t1 = newStartedThread(new InterruptedLockRunnable(lock));
+        waitForQueuedThread(lock, t1);
+        assertTrue(lock.hasQueuedThreads());
+        Thread t2 = newStartedThread(new InterruptibleLockRunnable(lock));
+        waitForQueuedThread(lock, t2);
+        assertTrue(lock.hasQueuedThreads());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertTrue(lock.hasQueuedThreads());
+        lock.release();
+        awaitTermination(t2);
+        assertFalse(lock.hasQueuedThreads());
+    }
+
+    /**
+     * getQueueLength reports number of waiting threads
+     */
+    public void testGetQueueLength()      { testGetQueueLength(false); }
+    public void testGetQueueLength_fair() { testGetQueueLength(true); }
+    public void testGetQueueLength(boolean fair) {
+        final PublicSemaphore lock = new PublicSemaphore(1, fair);
+        assertEquals(0, lock.getQueueLength());
+        lock.acquireUninterruptibly();
+        Thread t1 = newStartedThread(new InterruptedLockRunnable(lock));
+        waitForQueuedThread(lock, t1);
+        assertEquals(1, lock.getQueueLength());
+        Thread t2 = newStartedThread(new InterruptibleLockRunnable(lock));
+        waitForQueuedThread(lock, t2);
+        assertEquals(2, lock.getQueueLength());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertEquals(1, lock.getQueueLength());
+        lock.release();
+        awaitTermination(t2);
+        assertEquals(0, lock.getQueueLength());
+    }
+
+    /**
+     * getQueuedThreads includes waiting threads
+     */
+    public void testGetQueuedThreads()      { testGetQueuedThreads(false); }
+    public void testGetQueuedThreads_fair() { testGetQueuedThreads(true); }
+    public void testGetQueuedThreads(boolean fair) {
+        final PublicSemaphore lock = new PublicSemaphore(1, fair);
+        assertTrue(lock.getQueuedThreads().isEmpty());
+        lock.acquireUninterruptibly();
+        assertTrue(lock.getQueuedThreads().isEmpty());
+        Thread t1 = newStartedThread(new InterruptedLockRunnable(lock));
+        waitForQueuedThread(lock, t1);
+        assertTrue(lock.getQueuedThreads().contains(t1));
+        Thread t2 = newStartedThread(new InterruptibleLockRunnable(lock));
+        waitForQueuedThread(lock, t2);
+        assertTrue(lock.getQueuedThreads().contains(t1));
+        assertTrue(lock.getQueuedThreads().contains(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertFalse(lock.getQueuedThreads().contains(t1));
+        assertTrue(lock.getQueuedThreads().contains(t2));
+        lock.release();
+        awaitTermination(t2);
+        assertTrue(lock.getQueuedThreads().isEmpty());
+    }
+
+    /**
+     * drainPermits reports and removes given number of permits
+     */
+    public void testDrainPermits()      { testDrainPermits(false); }
+    public void testDrainPermits_fair() { testDrainPermits(true); }
+    public void testDrainPermits(boolean fair) {
+        Semaphore s = new Semaphore(0, fair);
+        assertEquals(0, s.availablePermits());
+        assertEquals(0, s.drainPermits());
+        s.release(10);
+        assertEquals(10, s.availablePermits());
+        assertEquals(10, s.drainPermits());
+        assertEquals(0, s.availablePermits());
+        assertEquals(0, s.drainPermits());
+    }
+
+    /**
+     * release(-N) throws IllegalArgumentException
+     */
+    public void testReleaseIAE()      { testReleaseIAE(false); }
+    public void testReleaseIAE_fair() { testReleaseIAE(true); }
+    public void testReleaseIAE(boolean fair) {
+        Semaphore s = new Semaphore(10, fair);
+        try {
+            s.release(-1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * reducePermits(-N) throws IllegalArgumentException
+     */
+    public void testReducePermitsIAE()      { testReducePermitsIAE(false); }
+    public void testReducePermitsIAE_fair() { testReducePermitsIAE(true); }
+    public void testReducePermitsIAE(boolean fair) {
+        PublicSemaphore s = new PublicSemaphore(10, fair);
+        try {
+            s.reducePermits(-1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * reducePermits reduces number of permits
+     */
+    public void testReducePermits()      { testReducePermits(false); }
+    public void testReducePermits_fair() { testReducePermits(true); }
+    public void testReducePermits(boolean fair) {
+        PublicSemaphore s = new PublicSemaphore(10, fair);
+        assertEquals(10, s.availablePermits());
+        s.reducePermits(0);
+        assertEquals(10, s.availablePermits());
+        s.reducePermits(1);
+        assertEquals(9, s.availablePermits());
+        s.reducePermits(10);
+        assertEquals(-1, s.availablePermits());
+        s.reducePermits(10);
+        assertEquals(-11, s.availablePermits());
+        s.reducePermits(0);
+        assertEquals(-11, s.availablePermits());
+    }
+
+    /**
+     * a reserialized semaphore has same number of permits and
+     * fairness, but no queued threads
+     */
+    public void testSerialization()      { testSerialization(false); }
+    public void testSerialization_fair() { testSerialization(true); }
+    public void testSerialization(boolean fair) {
+        try {
+            Semaphore s = new Semaphore(3, fair);
+            s.acquire();
+            s.acquire();
+            s.release();
+
+            Semaphore clone = serialClone(s);
+            assertEquals(fair, s.isFair());
+            assertEquals(fair, clone.isFair());
+            assertEquals(2, s.availablePermits());
+            assertEquals(2, clone.availablePermits());
+            clone.acquire();
+            clone.acquire();
+            clone.release();
+            assertEquals(2, s.availablePermits());
+            assertEquals(1, clone.availablePermits());
+            assertFalse(s.hasQueuedThreads());
+            assertFalse(clone.hasQueuedThreads());
+        } catch (InterruptedException e) { threadUnexpectedException(e); }
+
+        {
+            PublicSemaphore s = new PublicSemaphore(0, fair);
+            Thread t = newStartedThread(new InterruptibleLockRunnable(s));
+            // waitForQueuedThreads(s); // suffers from "flicker", so ...
+            waitForQueuedThread(s, t);  // ... we use this instead
+            PublicSemaphore clone = serialClone(s);
+            assertEquals(fair, s.isFair());
+            assertEquals(fair, clone.isFair());
+            assertEquals(0, s.availablePermits());
+            assertEquals(0, clone.availablePermits());
+            assertTrue(s.hasQueuedThreads());
+            assertFalse(clone.hasQueuedThreads());
+            s.release();
+            awaitTermination(t);
+            assertFalse(s.hasQueuedThreads());
+            assertFalse(clone.hasQueuedThreads());
+        }
+    }
+
+    /**
+     * tryAcquire(n) succeeds when sufficient permits, else fails
+     */
+    public void testTryAcquireNInSameThread()      { testTryAcquireNInSameThread(false); }
+    public void testTryAcquireNInSameThread_fair() { testTryAcquireNInSameThread(true); }
+    public void testTryAcquireNInSameThread(boolean fair) {
+        Semaphore s = new Semaphore(2, fair);
+        assertEquals(2, s.availablePermits());
+        assertFalse(s.tryAcquire(3));
+        assertEquals(2, s.availablePermits());
+        assertTrue(s.tryAcquire(2));
+        assertEquals(0, s.availablePermits());
+        assertFalse(s.tryAcquire(1));
+        assertFalse(s.tryAcquire(2));
+        assertEquals(0, s.availablePermits());
+    }
+
+    /**
+     * acquire succeeds if permits available
+     */
+    public void testReleaseAcquireSameThread_acquire()       { testReleaseAcquireSameThread(false, AcquireMethod.acquire); }
+    public void testReleaseAcquireSameThread_acquire_fair()  { testReleaseAcquireSameThread(true, AcquireMethod.acquire); }
+    public void testReleaseAcquireSameThread_acquireN()      { testReleaseAcquireSameThread(false, AcquireMethod.acquireN); }
+    public void testReleaseAcquireSameThread_acquireN_fair() { testReleaseAcquireSameThread(true, AcquireMethod.acquireN); }
+    public void testReleaseAcquireSameThread_acquireUninterruptibly()       { testReleaseAcquireSameThread(false, AcquireMethod.acquireUninterruptibly); }
+    public void testReleaseAcquireSameThread_acquireUninterruptibly_fair()  { testReleaseAcquireSameThread(true, AcquireMethod.acquireUninterruptibly); }
+    public void testReleaseAcquireSameThread_acquireUninterruptiblyN()      { testReleaseAcquireSameThread(false, AcquireMethod.acquireUninterruptibly); }
+    public void testReleaseAcquireSameThread_acquireUninterruptiblyN_fair() { testReleaseAcquireSameThread(true, AcquireMethod.acquireUninterruptibly); }
+    public void testReleaseAcquireSameThread_tryAcquire()       { testReleaseAcquireSameThread(false, AcquireMethod.tryAcquire); }
+    public void testReleaseAcquireSameThread_tryAcquire_fair()  { testReleaseAcquireSameThread(true, AcquireMethod.tryAcquire); }
+    public void testReleaseAcquireSameThread_tryAcquireN()      { testReleaseAcquireSameThread(false, AcquireMethod.tryAcquireN); }
+    public void testReleaseAcquireSameThread_tryAcquireN_fair() { testReleaseAcquireSameThread(true, AcquireMethod.tryAcquireN); }
+    public void testReleaseAcquireSameThread_tryAcquireTimed()       { testReleaseAcquireSameThread(false, AcquireMethod.tryAcquireTimed); }
+    public void testReleaseAcquireSameThread_tryAcquireTimed_fair()  { testReleaseAcquireSameThread(true, AcquireMethod.tryAcquireTimed); }
+    public void testReleaseAcquireSameThread_tryAcquireTimedN()      { testReleaseAcquireSameThread(false, AcquireMethod.tryAcquireTimedN); }
+    public void testReleaseAcquireSameThread_tryAcquireTimedN_fair() { testReleaseAcquireSameThread(true, AcquireMethod.tryAcquireTimedN); }
+    public void testReleaseAcquireSameThread(boolean fair,
+                                             final AcquireMethod acquirer) {
+        Semaphore s = new Semaphore(1, fair);
+        for (int i = 1; i < 6; i++) {
+            s.release(i);
+            assertEquals(1 + i, s.availablePermits());
+            try {
+                acquirer.acquire(s, i);
+            } catch (InterruptedException e) { threadUnexpectedException(e); }
+            assertEquals(1, s.availablePermits());
+        }
+    }
+
+    /**
+     * release in one thread enables acquire in another thread
+     */
+    public void testReleaseAcquireDifferentThreads_acquire()       { testReleaseAcquireDifferentThreads(false, AcquireMethod.acquire); }
+    public void testReleaseAcquireDifferentThreads_acquire_fair()  { testReleaseAcquireDifferentThreads(true, AcquireMethod.acquire); }
+    public void testReleaseAcquireDifferentThreads_acquireN()      { testReleaseAcquireDifferentThreads(false, AcquireMethod.acquireN); }
+    public void testReleaseAcquireDifferentThreads_acquireN_fair() { testReleaseAcquireDifferentThreads(true, AcquireMethod.acquireN); }
+    public void testReleaseAcquireDifferentThreads_acquireUninterruptibly()       { testReleaseAcquireDifferentThreads(false, AcquireMethod.acquireUninterruptibly); }
+    public void testReleaseAcquireDifferentThreads_acquireUninterruptibly_fair()  { testReleaseAcquireDifferentThreads(true, AcquireMethod.acquireUninterruptibly); }
+    public void testReleaseAcquireDifferentThreads_acquireUninterruptiblyN()      { testReleaseAcquireDifferentThreads(false, AcquireMethod.acquireUninterruptibly); }
+    public void testReleaseAcquireDifferentThreads_acquireUninterruptiblyN_fair() { testReleaseAcquireDifferentThreads(true, AcquireMethod.acquireUninterruptibly); }
+    public void testReleaseAcquireDifferentThreads_tryAcquireTimed()       { testReleaseAcquireDifferentThreads(false, AcquireMethod.tryAcquireTimed); }
+    public void testReleaseAcquireDifferentThreads_tryAcquireTimed_fair()  { testReleaseAcquireDifferentThreads(true, AcquireMethod.tryAcquireTimed); }
+    public void testReleaseAcquireDifferentThreads_tryAcquireTimedN()      { testReleaseAcquireDifferentThreads(false, AcquireMethod.tryAcquireTimedN); }
+    public void testReleaseAcquireDifferentThreads_tryAcquireTimedN_fair() { testReleaseAcquireDifferentThreads(true, AcquireMethod.tryAcquireTimedN); }
+    public void testReleaseAcquireDifferentThreads(boolean fair,
+                                                   final AcquireMethod acquirer) {
+        final Semaphore s = new Semaphore(0, fair);
+        final int rounds = 4;
+        long startTime = System.nanoTime();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < rounds; i++) {
+                    assertFalse(s.hasQueuedThreads());
+                    if (i % 2 == 0)
+                        acquirer.acquire(s);
+                    else
+                        acquirer.acquire(s, 3);
+                }}});
+
+        for (int i = 0; i < rounds; i++) {
+            while (! (s.availablePermits() == 0 && s.hasQueuedThreads()))
+                Thread.yield();
+            assertTrue(t.isAlive());
+            if (i % 2 == 0)
+                s.release();
+            else
+                s.release(3);
+        }
+        awaitTermination(t);
+        assertEquals(0, s.availablePermits());
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+    }
+
+    /**
+     * fair locks are strictly FIFO
+     */
+    public void testFairLocksFifo() {
+        final PublicSemaphore s = new PublicSemaphore(1, true);
+        final CountDownLatch pleaseRelease = new CountDownLatch(1);
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                // Will block; permits are available, but not three
+                s.acquire(3);
+            }});
+
+        waitForQueuedThread(s, t1);
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                // Will fail, even though 1 permit is available
+                assertFalse(s.tryAcquire(0L, MILLISECONDS));
+                assertFalse(s.tryAcquire(1, 0L, MILLISECONDS));
+
+                // untimed tryAcquire will barge and succeed
+                assertTrue(s.tryAcquire());
+                s.release(2);
+                assertTrue(s.tryAcquire(2));
+                s.release();
+
+                pleaseRelease.countDown();
+                // Will queue up behind t1, even though 1 permit is available
+                s.acquire();
+            }});
+
+        await(pleaseRelease);
+        waitForQueuedThread(s, t2);
+        s.release(2);
+        awaitTermination(t1);
+        assertTrue(t2.isAlive());
+        s.release();
+        awaitTermination(t2);
+    }
+
+    /**
+     * toString indicates current number of permits
+     */
+    public void testToString()      { testToString(false); }
+    public void testToString_fair() { testToString(true); }
+    public void testToString(boolean fair) {
+        PublicSemaphore s = new PublicSemaphore(0, fair);
+        assertTrue(s.toString().contains("Permits = 0"));
+        s.release();
+        assertTrue(s.toString().contains("Permits = 1"));
+        s.release(2);
+        assertTrue(s.toString().contains("Permits = 3"));
+        s.reducePermits(5);
+        assertTrue(s.toString().contains("Permits = -2"));
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/SplittableRandomTest.java b/jdk/test/java/util/concurrent/tck/SplittableRandomTest.java
new file mode 100644
index 00000000000..74baf387653
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/SplittableRandomTest.java
@@ -0,0 +1,555 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import java.util.SplittableRandom;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.LongAdder;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class SplittableRandomTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(SplittableRandomTest.class);
+    }
+
+    /*
+     * Testing coverage notes:
+     *
+     * 1. Many of the test methods are adapted from ThreadLocalRandomTest.
+     *
+     * 2. These tests do not check for random number generator quality.
+     * But we check for minimal API compliance by requiring that
+     * repeated calls to nextX methods, up to NCALLS tries, produce at
+     * least two distinct results. (In some possible universe, a
+     * "correct" implementation might fail, but the odds are vastly
+     * less than that of encountering a hardware failure while running
+     * the test.) For bounded nextX methods, we sample various
+     * intervals across multiples of primes. In other tests, we repeat
+     * under REPS different values.
+     */
+
+    // max numbers of calls to detect getting stuck on one value
+    static final int NCALLS = 10000;
+
+    // max sampled int bound
+    static final int MAX_INT_BOUND = (1 << 26);
+
+    // max sampled long bound
+    static final long MAX_LONG_BOUND = (1L << 40);
+
+    // Number of replications for other checks
+    static final int REPS =
+        Integer.getInteger("SplittableRandomTest.reps", 4);
+
+    /**
+     * Repeated calls to nextInt produce at least two distinct results
+     */
+    public void testNextInt() {
+        SplittableRandom sr = new SplittableRandom();
+        int f = sr.nextInt();
+        int i = 0;
+        while (i < NCALLS && sr.nextInt() == f)
+            ++i;
+        assertTrue(i < NCALLS);
+    }
+
+    /**
+     * Repeated calls to nextLong produce at least two distinct results
+     */
+    public void testNextLong() {
+        SplittableRandom sr = new SplittableRandom();
+        long f = sr.nextLong();
+        int i = 0;
+        while (i < NCALLS && sr.nextLong() == f)
+            ++i;
+        assertTrue(i < NCALLS);
+    }
+
+    /**
+     * Repeated calls to nextDouble produce at least two distinct results
+     */
+    public void testNextDouble() {
+        SplittableRandom sr = new SplittableRandom();
+        double f = sr.nextDouble();
+        int i = 0;
+        while (i < NCALLS && sr.nextDouble() == f)
+            ++i;
+        assertTrue(i < NCALLS);
+    }
+
+    /**
+     * Two SplittableRandoms created with the same seed produce the
+     * same values for nextLong.
+     */
+    public void testSeedConstructor() {
+        for (long seed = 2; seed < MAX_LONG_BOUND; seed += 15485863) {
+            SplittableRandom sr1 = new SplittableRandom(seed);
+            SplittableRandom sr2 = new SplittableRandom(seed);
+            for (int i = 0; i < REPS; ++i)
+                assertEquals(sr1.nextLong(), sr2.nextLong());
+        }
+    }
+
+    /**
+     * A SplittableRandom produced by split() of a default-constructed
+     * SplittableRandom generates a different sequence
+     */
+    public void testSplit1() {
+        SplittableRandom sr = new SplittableRandom();
+        for (int reps = 0; reps < REPS; ++reps) {
+            SplittableRandom sc = sr.split();
+            int i = 0;
+            while (i < NCALLS && sr.nextLong() == sc.nextLong())
+                ++i;
+            assertTrue(i < NCALLS);
+        }
+    }
+
+    /**
+     * A SplittableRandom produced by split() of a seeded-constructed
+     * SplittableRandom generates a different sequence
+     */
+    public void testSplit2() {
+        SplittableRandom sr = new SplittableRandom(12345);
+        for (int reps = 0; reps < REPS; ++reps) {
+            SplittableRandom sc = sr.split();
+            int i = 0;
+            while (i < NCALLS && sr.nextLong() == sc.nextLong())
+                ++i;
+            assertTrue(i < NCALLS);
+        }
+    }
+
+    /**
+     * nextInt(non-positive) throws IllegalArgumentException
+     */
+    public void testNextIntBoundNonPositive() {
+        SplittableRandom sr = new SplittableRandom();
+        Runnable[] throwingActions = {
+            () -> sr.nextInt(-17),
+            () -> sr.nextInt(0),
+            () -> sr.nextInt(Integer.MIN_VALUE),
+        };
+        assertThrows(IllegalArgumentException.class, throwingActions);
+    }
+
+    /**
+     * nextInt(least >= bound) throws IllegalArgumentException
+     */
+    public void testNextIntBadBounds() {
+        SplittableRandom sr = new SplittableRandom();
+        Runnable[] throwingActions = {
+            () -> sr.nextInt(17, 2),
+            () -> sr.nextInt(-42, -42),
+            () -> sr.nextInt(Integer.MAX_VALUE, Integer.MIN_VALUE),
+        };
+        assertThrows(IllegalArgumentException.class, throwingActions);
+    }
+
+    /**
+     * nextInt(bound) returns 0 <= value < bound;
+     * repeated calls produce at least two distinct results
+     */
+    public void testNextIntBounded() {
+        SplittableRandom sr = new SplittableRandom();
+        for (int i = 0; i < 2; i++) assertEquals(0, sr.nextInt(1));
+        // sample bound space across prime number increments
+        for (int bound = 2; bound < MAX_INT_BOUND; bound += 524959) {
+            int f = sr.nextInt(bound);
+            assertTrue(0 <= f && f < bound);
+            int i = 0;
+            int j;
+            while (i < NCALLS &&
+                   (j = sr.nextInt(bound)) == f) {
+                assertTrue(0 <= j && j < bound);
+                ++i;
+            }
+            assertTrue(i < NCALLS);
+        }
+    }
+
+    /**
+     * nextInt(least, bound) returns least <= value < bound;
+     * repeated calls produce at least two distinct results
+     */
+    public void testNextIntBounded2() {
+        SplittableRandom sr = new SplittableRandom();
+        for (int least = -15485863; least < MAX_INT_BOUND; least += 524959) {
+            for (int bound = least + 2; bound > least && bound < MAX_INT_BOUND; bound += 49979687) {
+                int f = sr.nextInt(least, bound);
+                assertTrue(least <= f && f < bound);
+                int i = 0;
+                int j;
+                while (i < NCALLS &&
+                       (j = sr.nextInt(least, bound)) == f) {
+                    assertTrue(least <= j && j < bound);
+                    ++i;
+                }
+                assertTrue(i < NCALLS);
+            }
+        }
+    }
+
+    /**
+     * nextLong(non-positive) throws IllegalArgumentException
+     */
+    public void testNextLongBoundNonPositive() {
+        SplittableRandom sr = new SplittableRandom();
+        Runnable[] throwingActions = {
+            () -> sr.nextLong(-17L),
+            () -> sr.nextLong(0L),
+            () -> sr.nextLong(Long.MIN_VALUE),
+        };
+        assertThrows(IllegalArgumentException.class, throwingActions);
+    }
+
+    /**
+     * nextLong(least >= bound) throws IllegalArgumentException
+     */
+    public void testNextLongBadBounds() {
+        SplittableRandom sr = new SplittableRandom();
+        Runnable[] throwingActions = {
+            () -> sr.nextLong(17L, 2L),
+            () -> sr.nextLong(-42L, -42L),
+            () -> sr.nextLong(Long.MAX_VALUE, Long.MIN_VALUE),
+        };
+        assertThrows(IllegalArgumentException.class, throwingActions);
+    }
+
+    /**
+     * nextLong(bound) returns 0 <= value < bound;
+     * repeated calls produce at least two distinct results
+     */
+    public void testNextLongBounded() {
+        SplittableRandom sr = new SplittableRandom();
+        for (int i = 0; i < 2; i++) assertEquals(0L, sr.nextLong(1L));
+        for (long bound = 2; bound < MAX_LONG_BOUND; bound += 15485863) {
+            long f = sr.nextLong(bound);
+            assertTrue(0 <= f && f < bound);
+            int i = 0;
+            long j;
+            while (i < NCALLS &&
+                   (j = sr.nextLong(bound)) == f) {
+                assertTrue(0 <= j && j < bound);
+                ++i;
+            }
+            assertTrue(i < NCALLS);
+        }
+    }
+
+    /**
+     * nextLong(least, bound) returns least <= value < bound;
+     * repeated calls produce at least two distinct results
+     */
+    public void testNextLongBounded2() {
+        SplittableRandom sr = new SplittableRandom();
+        for (long least = -86028121; least < MAX_LONG_BOUND; least += 982451653L) {
+            for (long bound = least + 2; bound > least && bound < MAX_LONG_BOUND; bound += Math.abs(bound * 7919)) {
+                long f = sr.nextLong(least, bound);
+                assertTrue(least <= f && f < bound);
+                int i = 0;
+                long j;
+                while (i < NCALLS &&
+                       (j = sr.nextLong(least, bound)) == f) {
+                    assertTrue(least <= j && j < bound);
+                    ++i;
+                }
+                assertTrue(i < NCALLS);
+            }
+        }
+    }
+
+    /**
+     * nextDouble(non-positive) throws IllegalArgumentException
+     */
+    public void testNextDoubleBoundNonPositive() {
+        SplittableRandom sr = new SplittableRandom();
+        Runnable[] throwingActions = {
+            () -> sr.nextDouble(-17.0d),
+            () -> sr.nextDouble(0.0d),
+            () -> sr.nextDouble(-Double.MIN_VALUE),
+            () -> sr.nextDouble(Double.NEGATIVE_INFINITY),
+            () -> sr.nextDouble(Double.NaN),
+        };
+        assertThrows(IllegalArgumentException.class, throwingActions);
+    }
+
+    /**
+     * nextDouble(! (least < bound)) throws IllegalArgumentException
+     */
+    public void testNextDoubleBadBounds() {
+        SplittableRandom sr = new SplittableRandom();
+        Runnable[] throwingActions = {
+            () -> sr.nextDouble(17.0d, 2.0d),
+            () -> sr.nextDouble(-42.0d, -42.0d),
+            () -> sr.nextDouble(Double.MAX_VALUE, Double.MIN_VALUE),
+            () -> sr.nextDouble(Double.NaN, 0.0d),
+            () -> sr.nextDouble(0.0d, Double.NaN),
+        };
+        assertThrows(IllegalArgumentException.class, throwingActions);
+    }
+
+    // TODO: Test infinite bounds!
+    //() -> sr.nextDouble(Double.NEGATIVE_INFINITY, 0.0d),
+    //() -> sr.nextDouble(0.0d, Double.POSITIVE_INFINITY),
+
+    /**
+     * nextDouble(least, bound) returns least <= value < bound;
+     * repeated calls produce at least two distinct results
+     */
+    public void testNextDoubleBounded2() {
+        SplittableRandom sr = new SplittableRandom();
+        for (double least = 0.0001; least < 1.0e20; least *= 8) {
+            for (double bound = least * 1.001; bound < 1.0e20; bound *= 16) {
+                double f = sr.nextDouble(least, bound);
+                assertTrue(least <= f && f < bound);
+                int i = 0;
+                double j;
+                while (i < NCALLS &&
+                       (j = sr.nextDouble(least, bound)) == f) {
+                    assertTrue(least <= j && j < bound);
+                    ++i;
+                }
+                assertTrue(i < NCALLS);
+            }
+        }
+    }
+
+    /**
+     * Invoking sized ints, long, doubles, with negative sizes throws
+     * IllegalArgumentException
+     */
+    public void testBadStreamSize() {
+        SplittableRandom r = new SplittableRandom();
+        Runnable[] throwingActions = {
+            () -> { java.util.stream.IntStream x = r.ints(-1L); },
+            () -> { java.util.stream.IntStream x = r.ints(-1L, 2, 3); },
+            () -> { java.util.stream.LongStream x = r.longs(-1L); },
+            () -> { java.util.stream.LongStream x = r.longs(-1L, -1L, 1L); },
+            () -> { java.util.stream.DoubleStream x = r.doubles(-1L); },
+            () -> { java.util.stream.DoubleStream x = r.doubles(-1L, .5, .6); },
+        };
+        assertThrows(IllegalArgumentException.class, throwingActions);
+    }
+
+    /**
+     * Invoking bounded ints, long, doubles, with illegal bounds throws
+     * IllegalArgumentException
+     */
+    public void testBadStreamBounds() {
+        SplittableRandom r = new SplittableRandom();
+        Runnable[] throwingActions = {
+            () -> { java.util.stream.IntStream x = r.ints(2, 1); },
+            () -> { java.util.stream.IntStream x = r.ints(10, 42, 42); },
+            () -> { java.util.stream.LongStream x = r.longs(-1L, -1L); },
+            () -> { java.util.stream.LongStream x = r.longs(10, 1L, -2L); },
+            () -> { java.util.stream.DoubleStream x = r.doubles(0.0, 0.0); },
+            () -> { java.util.stream.DoubleStream x = r.doubles(10, .5, .4); },
+        };
+        assertThrows(IllegalArgumentException.class, throwingActions);
+    }
+
+    /**
+     * A parallel sized stream of ints generates the given number of values
+     */
+    public void testIntsCount() {
+        LongAdder counter = new LongAdder();
+        SplittableRandom r = new SplittableRandom();
+        long size = 0;
+        for (int reps = 0; reps < REPS; ++reps) {
+            counter.reset();
+            r.ints(size).parallel().forEach(x -> counter.increment());
+            assertEquals(size, counter.sum());
+            size += 524959;
+        }
+    }
+
+    /**
+     * A parallel sized stream of longs generates the given number of values
+     */
+    public void testLongsCount() {
+        LongAdder counter = new LongAdder();
+        SplittableRandom r = new SplittableRandom();
+        long size = 0;
+        for (int reps = 0; reps < REPS; ++reps) {
+            counter.reset();
+            r.longs(size).parallel().forEach(x -> counter.increment());
+            assertEquals(size, counter.sum());
+            size += 524959;
+        }
+    }
+
+    /**
+     * A parallel sized stream of doubles generates the given number of values
+     */
+    public void testDoublesCount() {
+        LongAdder counter = new LongAdder();
+        SplittableRandom r = new SplittableRandom();
+        long size = 0;
+        for (int reps = 0; reps < REPS; ++reps) {
+            counter.reset();
+            r.doubles(size).parallel().forEach(x -> counter.increment());
+            assertEquals(size, counter.sum());
+            size += 524959;
+        }
+    }
+
+    /**
+     * Each of a parallel sized stream of bounded ints is within bounds
+     */
+    public void testBoundedInts() {
+        AtomicInteger fails = new AtomicInteger(0);
+        SplittableRandom r = new SplittableRandom();
+        long size = 12345L;
+        for (int least = -15485867; least < MAX_INT_BOUND; least += 524959) {
+            for (int bound = least + 2; bound > least && bound < MAX_INT_BOUND; bound += 67867967) {
+                final int lo = least, hi = bound;
+                r.ints(size, lo, hi).parallel().forEach(
+                    x -> {
+                        if (x < lo || x >= hi)
+                            fails.getAndIncrement(); });
+            }
+        }
+        assertEquals(0, fails.get());
+    }
+
+    /**
+     * Each of a parallel sized stream of bounded longs is within bounds
+     */
+    public void testBoundedLongs() {
+        AtomicInteger fails = new AtomicInteger(0);
+        SplittableRandom r = new SplittableRandom();
+        long size = 123L;
+        for (long least = -86028121; least < MAX_LONG_BOUND; least += 1982451653L) {
+            for (long bound = least + 2; bound > least && bound < MAX_LONG_BOUND; bound += Math.abs(bound * 7919)) {
+                final long lo = least, hi = bound;
+                r.longs(size, lo, hi).parallel().forEach(
+                    x -> {
+                        if (x < lo || x >= hi)
+                            fails.getAndIncrement(); });
+            }
+        }
+        assertEquals(0, fails.get());
+    }
+
+    /**
+     * Each of a parallel sized stream of bounded doubles is within bounds
+     */
+    public void testBoundedDoubles() {
+        AtomicInteger fails = new AtomicInteger(0);
+        SplittableRandom r = new SplittableRandom();
+        long size = 456;
+        for (double least = 0.00011; least < 1.0e20; least *= 9) {
+            for (double bound = least * 1.0011; bound < 1.0e20; bound *= 17) {
+                final double lo = least, hi = bound;
+                r.doubles(size, lo, hi).parallel().forEach(
+                    x -> {
+                        if (x < lo || x >= hi)
+                            fails.getAndIncrement(); });
+            }
+        }
+        assertEquals(0, fails.get());
+    }
+
+    /**
+     * A parallel unsized stream of ints generates at least 100 values
+     */
+    public void testUnsizedIntsCount() {
+        LongAdder counter = new LongAdder();
+        SplittableRandom r = new SplittableRandom();
+        long size = 100;
+        r.ints().limit(size).parallel().forEach(x -> counter.increment());
+        assertEquals(size, counter.sum());
+    }
+
+    /**
+     * A parallel unsized stream of longs generates at least 100 values
+     */
+    public void testUnsizedLongsCount() {
+        LongAdder counter = new LongAdder();
+        SplittableRandom r = new SplittableRandom();
+        long size = 100;
+        r.longs().limit(size).parallel().forEach(x -> counter.increment());
+        assertEquals(size, counter.sum());
+    }
+
+    /**
+     * A parallel unsized stream of doubles generates at least 100 values
+     */
+    public void testUnsizedDoublesCount() {
+        LongAdder counter = new LongAdder();
+        SplittableRandom r = new SplittableRandom();
+        long size = 100;
+        r.doubles().limit(size).parallel().forEach(x -> counter.increment());
+        assertEquals(size, counter.sum());
+    }
+
+    /**
+     * A sequential unsized stream of ints generates at least 100 values
+     */
+    public void testUnsizedIntsCountSeq() {
+        LongAdder counter = new LongAdder();
+        SplittableRandom r = new SplittableRandom();
+        long size = 100;
+        r.ints().limit(size).forEach(x -> counter.increment());
+        assertEquals(size, counter.sum());
+    }
+
+    /**
+     * A sequential unsized stream of longs generates at least 100 values
+     */
+    public void testUnsizedLongsCountSeq() {
+        LongAdder counter = new LongAdder();
+        SplittableRandom r = new SplittableRandom();
+        long size = 100;
+        r.longs().limit(size).forEach(x -> counter.increment());
+        assertEquals(size, counter.sum());
+    }
+
+    /**
+     * A sequential unsized stream of doubles generates at least 100 values
+     */
+    public void testUnsizedDoublesCountSeq() {
+        LongAdder counter = new LongAdder();
+        SplittableRandom r = new SplittableRandom();
+        long size = 100;
+        r.doubles().limit(size).forEach(x -> counter.increment());
+        assertEquals(size, counter.sum());
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/StampedLockTest.java b/jdk/test/java/util/concurrent/tck/StampedLockTest.java
new file mode 100644
index 00000000000..84026409d7b
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/StampedLockTest.java
@@ -0,0 +1,906 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea and Martin Buchholz
+ * with assistance from members of JCP JSR-166 Expert Group and
+ * released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.StampedLock;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class StampedLockTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(StampedLockTest.class);
+    }
+
+    /**
+     * A runnable calling writeLockInterruptibly
+     */
+    class InterruptibleLockRunnable extends CheckedRunnable {
+        final StampedLock lock;
+        InterruptibleLockRunnable(StampedLock l) { lock = l; }
+        public void realRun() throws InterruptedException {
+            lock.writeLockInterruptibly();
+        }
+    }
+
+    /**
+     * A runnable calling writeLockInterruptibly that expects to be
+     * interrupted
+     */
+    class InterruptedLockRunnable extends CheckedInterruptedRunnable {
+        final StampedLock lock;
+        InterruptedLockRunnable(StampedLock l) { lock = l; }
+        public void realRun() throws InterruptedException {
+            lock.writeLockInterruptibly();
+        }
+    }
+
+    /**
+     * Releases write lock, checking isWriteLocked before and after
+     */
+    void releaseWriteLock(StampedLock lock, long s) {
+        assertTrue(lock.isWriteLocked());
+        lock.unlockWrite(s);
+        assertFalse(lock.isWriteLocked());
+    }
+
+    /**
+     * Constructed StampedLock is in unlocked state
+     */
+    public void testConstructor() {
+        StampedLock lock;
+        lock = new StampedLock();
+        assertFalse(lock.isWriteLocked());
+        assertFalse(lock.isReadLocked());
+        assertEquals(lock.getReadLockCount(), 0);
+    }
+
+    /**
+     * write-locking and read-locking an unlocked lock succeed
+     */
+    public void testLock() {
+        StampedLock lock = new StampedLock();
+        assertFalse(lock.isWriteLocked());
+        assertFalse(lock.isReadLocked());
+        assertEquals(lock.getReadLockCount(), 0);
+        long s = lock.writeLock();
+        assertTrue(lock.isWriteLocked());
+        assertFalse(lock.isReadLocked());
+        assertEquals(lock.getReadLockCount(), 0);
+        lock.unlockWrite(s);
+        assertFalse(lock.isWriteLocked());
+        assertFalse(lock.isReadLocked());
+        assertEquals(lock.getReadLockCount(), 0);
+        long rs = lock.readLock();
+        assertFalse(lock.isWriteLocked());
+        assertTrue(lock.isReadLocked());
+        assertEquals(lock.getReadLockCount(), 1);
+        lock.unlockRead(rs);
+        assertFalse(lock.isWriteLocked());
+        assertFalse(lock.isReadLocked());
+        assertEquals(lock.getReadLockCount(), 0);
+    }
+
+    /**
+     * unlock releases either a read or write lock
+     */
+    public void testUnlock() {
+        StampedLock lock = new StampedLock();
+        assertFalse(lock.isWriteLocked());
+        assertFalse(lock.isReadLocked());
+        assertEquals(lock.getReadLockCount(), 0);
+        long s = lock.writeLock();
+        assertTrue(lock.isWriteLocked());
+        assertFalse(lock.isReadLocked());
+        assertEquals(lock.getReadLockCount(), 0);
+        lock.unlock(s);
+        assertFalse(lock.isWriteLocked());
+        assertFalse(lock.isReadLocked());
+        assertEquals(lock.getReadLockCount(), 0);
+        long rs = lock.readLock();
+        assertFalse(lock.isWriteLocked());
+        assertTrue(lock.isReadLocked());
+        assertEquals(lock.getReadLockCount(), 1);
+        lock.unlock(rs);
+        assertFalse(lock.isWriteLocked());
+        assertFalse(lock.isReadLocked());
+        assertEquals(lock.getReadLockCount(), 0);
+    }
+
+    /**
+     * tryUnlockRead/Write succeeds if locked in associated mode else
+     * returns false
+     */
+    public void testTryUnlock() {
+        StampedLock lock = new StampedLock();
+        assertFalse(lock.isWriteLocked());
+        assertFalse(lock.isReadLocked());
+        assertEquals(lock.getReadLockCount(), 0);
+        long s = lock.writeLock();
+        assertTrue(lock.isWriteLocked());
+        assertFalse(lock.isReadLocked());
+        assertEquals(lock.getReadLockCount(), 0);
+        assertFalse(lock.tryUnlockRead());
+        assertTrue(lock.tryUnlockWrite());
+        assertFalse(lock.tryUnlockWrite());
+        assertFalse(lock.tryUnlockRead());
+        assertFalse(lock.isWriteLocked());
+        assertFalse(lock.isReadLocked());
+        assertEquals(lock.getReadLockCount(), 0);
+        long rs = lock.readLock();
+        assertFalse(lock.isWriteLocked());
+        assertTrue(lock.isReadLocked());
+        assertEquals(lock.getReadLockCount(), 1);
+        assertFalse(lock.tryUnlockWrite());
+        assertTrue(lock.tryUnlockRead());
+        assertFalse(lock.tryUnlockRead());
+        assertFalse(lock.tryUnlockWrite());
+        assertFalse(lock.isWriteLocked());
+        assertFalse(lock.isReadLocked());
+        assertEquals(lock.getReadLockCount(), 0);
+    }
+
+    /**
+     * write-unlocking an unlocked lock throws IllegalMonitorStateException
+     */
+    public void testWriteUnlock_IMSE() {
+        StampedLock lock = new StampedLock();
+        try {
+            lock.unlockWrite(0L);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * write-unlocking an unlocked lock throws IllegalMonitorStateException
+     */
+    public void testWriteUnlock_IMSE2() {
+        StampedLock lock = new StampedLock();
+        long s = lock.writeLock();
+        lock.unlockWrite(s);
+        try {
+            lock.unlockWrite(s);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * write-unlocking after readlock throws IllegalMonitorStateException
+     */
+    public void testWriteUnlock_IMSE3() {
+        StampedLock lock = new StampedLock();
+        long s = lock.readLock();
+        try {
+            lock.unlockWrite(s);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * read-unlocking an unlocked lock throws IllegalMonitorStateException
+     */
+    public void testReadUnlock_IMSE() {
+        StampedLock lock = new StampedLock();
+        long s = lock.readLock();
+        lock.unlockRead(s);
+        try {
+            lock.unlockRead(s);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * read-unlocking an unlocked lock throws IllegalMonitorStateException
+     */
+    public void testReadUnlock_IMSE2() {
+        StampedLock lock = new StampedLock();
+        try {
+            lock.unlockRead(0L);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * read-unlocking after writeLock throws IllegalMonitorStateException
+     */
+    public void testReadUnlock_IMSE3() {
+        StampedLock lock = new StampedLock();
+        long s = lock.writeLock();
+        try {
+            lock.unlockRead(s);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * validate(0) fails
+     */
+    public void testValidate0() {
+        StampedLock lock = new StampedLock();
+        assertFalse(lock.validate(0L));
+    }
+
+    /**
+     * A stamp obtained from a successful lock operation validates
+     */
+    public void testValidate() throws InterruptedException {
+        StampedLock lock = new StampedLock();
+        long s = lock.writeLock();
+        assertTrue(lock.validate(s));
+        lock.unlockWrite(s);
+        s = lock.readLock();
+        assertTrue(lock.validate(s));
+        lock.unlockRead(s);
+        assertTrue((s = lock.tryWriteLock()) != 0L);
+        assertTrue(lock.validate(s));
+        lock.unlockWrite(s);
+        assertTrue((s = lock.tryReadLock()) != 0L);
+        assertTrue(lock.validate(s));
+        lock.unlockRead(s);
+        assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L);
+        assertTrue(lock.validate(s));
+        lock.unlockWrite(s);
+        assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L);
+        assertTrue(lock.validate(s));
+        lock.unlockRead(s);
+        assertTrue((s = lock.tryOptimisticRead()) != 0L);
+    }
+
+    /**
+     * A stamp obtained from an unsuccessful lock operation does not validate
+     */
+    public void testValidate2() throws InterruptedException {
+        StampedLock lock = new StampedLock();
+        long s;
+        assertTrue((s = lock.writeLock()) != 0L);
+        assertTrue(lock.validate(s));
+        assertFalse(lock.validate(lock.tryWriteLock()));
+        assertFalse(lock.validate(lock.tryWriteLock(10L, MILLISECONDS)));
+        assertFalse(lock.validate(lock.tryReadLock()));
+        assertFalse(lock.validate(lock.tryReadLock(10L, MILLISECONDS)));
+        assertFalse(lock.validate(lock.tryOptimisticRead()));
+        lock.unlockWrite(s);
+    }
+
+    /**
+     * writeLockInterruptibly is interruptible
+     */
+    public void testWriteLockInterruptibly_Interruptible()
+            throws InterruptedException {
+        final CountDownLatch running = new CountDownLatch(1);
+        final StampedLock lock = new StampedLock();
+        long s = lock.writeLock();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                running.countDown();
+                lock.writeLockInterruptibly();
+            }});
+
+        running.await();
+        waitForThreadToEnterWaitState(t, 100);
+        t.interrupt();
+        awaitTermination(t);
+        releaseWriteLock(lock, s);
+    }
+
+    /**
+     * timed tryWriteLock is interruptible
+     */
+    public void testWriteTryLock_Interruptible() throws InterruptedException {
+        final CountDownLatch running = new CountDownLatch(1);
+        final StampedLock lock = new StampedLock();
+        long s = lock.writeLock();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                running.countDown();
+                lock.tryWriteLock(2 * LONG_DELAY_MS, MILLISECONDS);
+            }});
+
+        running.await();
+        waitForThreadToEnterWaitState(t, 100);
+        t.interrupt();
+        awaitTermination(t);
+        releaseWriteLock(lock, s);
+    }
+
+    /**
+     * readLockInterruptibly is interruptible
+     */
+    public void testReadLockInterruptibly_Interruptible()
+            throws InterruptedException {
+        final CountDownLatch running = new CountDownLatch(1);
+        final StampedLock lock = new StampedLock();
+        long s = lock.writeLock();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                running.countDown();
+                lock.readLockInterruptibly();
+            }});
+
+        running.await();
+        waitForThreadToEnterWaitState(t, 100);
+        t.interrupt();
+        awaitTermination(t);
+        releaseWriteLock(lock, s);
+    }
+
+    /**
+     * timed tryReadLock is interruptible
+     */
+    public void testReadTryLock_Interruptible() throws InterruptedException {
+        final CountDownLatch running = new CountDownLatch(1);
+        final StampedLock lock = new StampedLock();
+        long s = lock.writeLock();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                running.countDown();
+                lock.tryReadLock(2 * LONG_DELAY_MS, MILLISECONDS);
+            }});
+
+        running.await();
+        waitForThreadToEnterWaitState(t, 100);
+        t.interrupt();
+        awaitTermination(t);
+        releaseWriteLock(lock, s);
+    }
+
+    /**
+     * tryWriteLock on an unlocked lock succeeds
+     */
+    public void testWriteTryLock() {
+        final StampedLock lock = new StampedLock();
+        long s = lock.tryWriteLock();
+        assertTrue(s != 0L);
+        assertTrue(lock.isWriteLocked());
+        long s2 = lock.tryWriteLock();
+        assertEquals(s2, 0L);
+        releaseWriteLock(lock, s);
+    }
+
+    /**
+     * tryWriteLock fails if locked
+     */
+    public void testWriteTryLockWhenLocked() {
+        final StampedLock lock = new StampedLock();
+        long s = lock.writeLock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                long ws = lock.tryWriteLock();
+                assertTrue(ws == 0L);
+            }});
+
+        awaitTermination(t);
+        releaseWriteLock(lock, s);
+    }
+
+    /**
+     * tryReadLock fails if write-locked
+     */
+    public void testReadTryLockWhenLocked() {
+        final StampedLock lock = new StampedLock();
+        long s = lock.writeLock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                long rs = lock.tryReadLock();
+                assertEquals(rs, 0L);
+            }});
+
+        awaitTermination(t);
+        releaseWriteLock(lock, s);
+    }
+
+    /**
+     * Multiple threads can hold a read lock when not write-locked
+     */
+    public void testMultipleReadLocks() {
+        final StampedLock lock = new StampedLock();
+        final long s = lock.readLock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long s2 = lock.tryReadLock();
+                assertTrue(s2 != 0L);
+                lock.unlockRead(s2);
+                long s3 = lock.tryReadLock(LONG_DELAY_MS, MILLISECONDS);
+                assertTrue(s3 != 0L);
+                lock.unlockRead(s3);
+                long s4 = lock.readLock();
+                lock.unlockRead(s4);
+            }});
+
+        awaitTermination(t);
+        lock.unlockRead(s);
+    }
+
+    /**
+     * A writelock succeeds only after a reading thread unlocks
+     */
+    public void testWriteAfterReadLock() throws InterruptedException {
+        final CountDownLatch running = new CountDownLatch(1);
+        final StampedLock lock = new StampedLock();
+        long rs = lock.readLock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                running.countDown();
+                long s = lock.writeLock();
+                lock.unlockWrite(s);
+            }});
+
+        running.await();
+        waitForThreadToEnterWaitState(t, 100);
+        assertFalse(lock.isWriteLocked());
+        lock.unlockRead(rs);
+        awaitTermination(t);
+        assertFalse(lock.isWriteLocked());
+    }
+
+    /**
+     * A writelock succeeds only after reading threads unlock
+     */
+    public void testWriteAfterMultipleReadLocks() {
+        final StampedLock lock = new StampedLock();
+        long s = lock.readLock();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                long rs = lock.readLock();
+                lock.unlockRead(rs);
+            }});
+
+        awaitTermination(t1);
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                long ws = lock.writeLock();
+                lock.unlockWrite(ws);
+            }});
+
+        assertFalse(lock.isWriteLocked());
+        lock.unlockRead(s);
+        awaitTermination(t2);
+        assertFalse(lock.isWriteLocked());
+    }
+
+    /**
+     * Readlocks succeed only after a writing thread unlocks
+     */
+    public void testReadAfterWriteLock() {
+        final StampedLock lock = new StampedLock();
+        final long s = lock.writeLock();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                long rs = lock.readLock();
+                lock.unlockRead(rs);
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                long rs = lock.readLock();
+                lock.unlockRead(rs);
+            }});
+
+        releaseWriteLock(lock, s);
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * tryReadLock succeeds if readlocked but not writelocked
+     */
+    public void testTryLockWhenReadLocked() {
+        final StampedLock lock = new StampedLock();
+        long s = lock.readLock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                long rs = lock.tryReadLock();
+                threadAssertTrue(rs != 0L);
+                lock.unlockRead(rs);
+            }});
+
+        awaitTermination(t);
+        lock.unlockRead(s);
+    }
+
+    /**
+     * tryWriteLock fails when readlocked
+     */
+    public void testWriteTryLockWhenReadLocked() {
+        final StampedLock lock = new StampedLock();
+        long s = lock.readLock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                long ws = lock.tryWriteLock();
+                threadAssertEquals(ws, 0L);
+            }});
+
+        awaitTermination(t);
+        lock.unlockRead(s);
+    }
+
+    /**
+     * timed tryWriteLock times out if locked
+     */
+    public void testWriteTryLock_Timeout() {
+        final StampedLock lock = new StampedLock();
+        long s = lock.writeLock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                long timeoutMillis = 10;
+                long ws = lock.tryWriteLock(timeoutMillis, MILLISECONDS);
+                assertEquals(ws, 0L);
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+            }});
+
+        awaitTermination(t);
+        releaseWriteLock(lock, s);
+    }
+
+    /**
+     * timed tryReadLock times out if write-locked
+     */
+    public void testReadTryLock_Timeout() {
+        final StampedLock lock = new StampedLock();
+        long s = lock.writeLock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                long timeoutMillis = 10;
+                long rs = lock.tryReadLock(timeoutMillis, MILLISECONDS);
+                assertEquals(rs, 0L);
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+            }});
+
+        awaitTermination(t);
+        assertTrue(lock.isWriteLocked());
+        lock.unlockWrite(s);
+    }
+
+    /**
+     * writeLockInterruptibly succeeds if unlocked, else is interruptible
+     */
+    public void testWriteLockInterruptibly() throws InterruptedException {
+        final CountDownLatch running = new CountDownLatch(1);
+        final StampedLock lock = new StampedLock();
+        long s = lock.writeLockInterruptibly();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                running.countDown();
+                lock.writeLockInterruptibly();
+            }});
+
+        running.await();
+        waitForThreadToEnterWaitState(t, 100);
+        t.interrupt();
+        assertTrue(lock.isWriteLocked());
+        awaitTermination(t);
+        releaseWriteLock(lock, s);
+    }
+
+    /**
+     * readLockInterruptibly succeeds if lock free else is interruptible
+     */
+    public void testReadLockInterruptibly() throws InterruptedException {
+        final CountDownLatch running = new CountDownLatch(1);
+        final StampedLock lock = new StampedLock();
+        long s;
+        s = lock.readLockInterruptibly();
+        lock.unlockRead(s);
+        s = lock.writeLockInterruptibly();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                running.countDown();
+                lock.readLockInterruptibly();
+            }});
+
+        running.await();
+        waitForThreadToEnterWaitState(t, 100);
+        t.interrupt();
+        awaitTermination(t);
+        releaseWriteLock(lock, s);
+    }
+
+    /**
+     * A serialized lock deserializes as unlocked
+     */
+    public void testSerialization() {
+        StampedLock lock = new StampedLock();
+        lock.writeLock();
+        StampedLock clone = serialClone(lock);
+        assertTrue(lock.isWriteLocked());
+        assertFalse(clone.isWriteLocked());
+        long s = clone.writeLock();
+        assertTrue(clone.isWriteLocked());
+        clone.unlockWrite(s);
+        assertFalse(clone.isWriteLocked());
+    }
+
+    /**
+     * toString indicates current lock state
+     */
+    public void testToString() {
+        StampedLock lock = new StampedLock();
+        assertTrue(lock.toString().contains("Unlocked"));
+        long s = lock.writeLock();
+        assertTrue(lock.toString().contains("Write-locked"));
+        lock.unlockWrite(s);
+        s = lock.readLock();
+        assertTrue(lock.toString().contains("Read-locks"));
+    }
+
+    /**
+     * tryOptimisticRead succeeds and validates if unlocked, fails if locked
+     */
+    public void testValidateOptimistic() throws InterruptedException {
+        StampedLock lock = new StampedLock();
+        long s, p;
+        assertTrue((p = lock.tryOptimisticRead()) != 0L);
+        assertTrue(lock.validate(p));
+        assertTrue((s = lock.writeLock()) != 0L);
+        assertFalse((p = lock.tryOptimisticRead()) != 0L);
+        assertTrue(lock.validate(s));
+        lock.unlockWrite(s);
+        assertTrue((p = lock.tryOptimisticRead()) != 0L);
+        assertTrue(lock.validate(p));
+        assertTrue((s = lock.readLock()) != 0L);
+        assertTrue(lock.validate(s));
+        assertTrue((p = lock.tryOptimisticRead()) != 0L);
+        assertTrue(lock.validate(p));
+        lock.unlockRead(s);
+        assertTrue((s = lock.tryWriteLock()) != 0L);
+        assertTrue(lock.validate(s));
+        assertFalse((p = lock.tryOptimisticRead()) != 0L);
+        lock.unlockWrite(s);
+        assertTrue((s = lock.tryReadLock()) != 0L);
+        assertTrue(lock.validate(s));
+        assertTrue((p = lock.tryOptimisticRead()) != 0L);
+        lock.unlockRead(s);
+        assertTrue(lock.validate(p));
+        assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L);
+        assertFalse((p = lock.tryOptimisticRead()) != 0L);
+        assertTrue(lock.validate(s));
+        lock.unlockWrite(s);
+        assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L);
+        assertTrue(lock.validate(s));
+        assertTrue((p = lock.tryOptimisticRead()) != 0L);
+        lock.unlockRead(s);
+        assertTrue((p = lock.tryOptimisticRead()) != 0L);
+    }
+
+    /**
+     * tryOptimisticRead stamp does not validate if a write lock intervenes
+     */
+    public void testValidateOptimisticWriteLocked() {
+        StampedLock lock = new StampedLock();
+        long s, p;
+        assertTrue((p = lock.tryOptimisticRead()) != 0L);
+        assertTrue((s = lock.writeLock()) != 0L);
+        assertFalse(lock.validate(p));
+        assertFalse((p = lock.tryOptimisticRead()) != 0L);
+        assertTrue(lock.validate(s));
+        lock.unlockWrite(s);
+    }
+
+    /**
+     * tryOptimisticRead stamp does not validate if a write lock
+     * intervenes in another thread
+     */
+    public void testValidateOptimisticWriteLocked2()
+            throws InterruptedException {
+        final CountDownLatch running = new CountDownLatch(1);
+        final StampedLock lock = new StampedLock();
+        long s, p;
+        assertTrue((p = lock.tryOptimisticRead()) != 0L);
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLockInterruptibly();
+                running.countDown();
+                lock.writeLockInterruptibly();
+            }});
+
+        running.await();
+        assertFalse(lock.validate(p));
+        assertFalse((p = lock.tryOptimisticRead()) != 0L);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * tryConvertToOptimisticRead succeeds and validates if successfully locked,
+     */
+    public void testTryConvertToOptimisticRead() throws InterruptedException {
+        StampedLock lock = new StampedLock();
+        long s, p;
+        s = 0L;
+        assertFalse((p = lock.tryConvertToOptimisticRead(s)) != 0L);
+        assertTrue((s = lock.tryOptimisticRead()) != 0L);
+        assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
+        assertTrue((s = lock.writeLock()) != 0L);
+        assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
+        assertTrue(lock.validate(p));
+        assertTrue((s = lock.readLock()) != 0L);
+        assertTrue(lock.validate(s));
+        assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
+        assertTrue(lock.validate(p));
+        assertTrue((s = lock.tryWriteLock()) != 0L);
+        assertTrue(lock.validate(s));
+        assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
+        assertTrue(lock.validate(p));
+        assertTrue((s = lock.tryReadLock()) != 0L);
+        assertTrue(lock.validate(s));
+        assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
+        assertTrue(lock.validate(p));
+        assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L);
+        assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
+        assertTrue(lock.validate(p));
+        assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L);
+        assertTrue(lock.validate(s));
+        assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
+        assertTrue(lock.validate(p));
+    }
+
+    /**
+     * tryConvertToReadLock succeeds and validates if successfully locked
+     * or lock free;
+     */
+    public void testTryConvertToReadLock() throws InterruptedException {
+        StampedLock lock = new StampedLock();
+        long s, p;
+        s = 0L;
+        assertFalse((p = lock.tryConvertToReadLock(s)) != 0L);
+        assertTrue((s = lock.tryOptimisticRead()) != 0L);
+        assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
+        lock.unlockRead(p);
+        assertTrue((s = lock.writeLock()) != 0L);
+        assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
+        assertTrue(lock.validate(p));
+        lock.unlockRead(p);
+        assertTrue((s = lock.readLock()) != 0L);
+        assertTrue(lock.validate(s));
+        assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
+        assertTrue(lock.validate(p));
+        lock.unlockRead(p);
+        assertTrue((s = lock.tryWriteLock()) != 0L);
+        assertTrue(lock.validate(s));
+        assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
+        assertTrue(lock.validate(p));
+        lock.unlockRead(p);
+        assertTrue((s = lock.tryReadLock()) != 0L);
+        assertTrue(lock.validate(s));
+        assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
+        assertTrue(lock.validate(p));
+        lock.unlockRead(p);
+        assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L);
+        assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
+        assertTrue(lock.validate(p));
+        lock.unlockRead(p);
+        assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L);
+        assertTrue(lock.validate(s));
+        assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
+        assertTrue(lock.validate(p));
+        lock.unlockRead(p);
+    }
+
+    /**
+     * tryConvertToWriteLock succeeds and validates if successfully locked
+     * or lock free;
+     */
+    public void testTryConvertToWriteLock() throws InterruptedException {
+        StampedLock lock = new StampedLock();
+        long s, p;
+        s = 0L;
+        assertFalse((p = lock.tryConvertToWriteLock(s)) != 0L);
+        assertTrue((s = lock.tryOptimisticRead()) != 0L);
+        assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
+        lock.unlockWrite(p);
+        assertTrue((s = lock.writeLock()) != 0L);
+        assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
+        assertTrue(lock.validate(p));
+        lock.unlockWrite(p);
+        assertTrue((s = lock.readLock()) != 0L);
+        assertTrue(lock.validate(s));
+        assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
+        assertTrue(lock.validate(p));
+        lock.unlockWrite(p);
+        assertTrue((s = lock.tryWriteLock()) != 0L);
+        assertTrue(lock.validate(s));
+        assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
+        assertTrue(lock.validate(p));
+        lock.unlockWrite(p);
+        assertTrue((s = lock.tryReadLock()) != 0L);
+        assertTrue(lock.validate(s));
+        assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
+        assertTrue(lock.validate(p));
+        lock.unlockWrite(p);
+        assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L);
+        assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
+        assertTrue(lock.validate(p));
+        lock.unlockWrite(p);
+        assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L);
+        assertTrue(lock.validate(s));
+        assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
+        assertTrue(lock.validate(p));
+        lock.unlockWrite(p);
+    }
+
+    /**
+     * asWriteLock can be locked and unlocked
+     */
+    public void testAsWriteLock() {
+        StampedLock sl = new StampedLock();
+        Lock lock = sl.asWriteLock();
+        lock.lock();
+        assertFalse(lock.tryLock());
+        lock.unlock();
+        assertTrue(lock.tryLock());
+    }
+
+    /**
+     * asReadLock can be locked and unlocked
+     */
+    public void testAsReadLock() {
+        StampedLock sl = new StampedLock();
+        Lock lock = sl.asReadLock();
+        lock.lock();
+        lock.unlock();
+        assertTrue(lock.tryLock());
+    }
+
+    /**
+     * asReadWriteLock.writeLock can be locked and unlocked
+     */
+    public void testAsReadWriteLockWriteLock() {
+        StampedLock sl = new StampedLock();
+        Lock lock = sl.asReadWriteLock().writeLock();
+        lock.lock();
+        assertFalse(lock.tryLock());
+        lock.unlock();
+        assertTrue(lock.tryLock());
+    }
+
+    /**
+     * asReadWriteLock.readLock can be locked and unlocked
+     */
+    public void testAsReadWriteLockReadLock() {
+        StampedLock sl = new StampedLock();
+        Lock lock = sl.asReadWriteLock().readLock();
+        lock.lock();
+        lock.unlock();
+        assertTrue(lock.tryLock());
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/SubmissionPublisherTest.java b/jdk/test/java/util/concurrent/tck/SubmissionPublisherTest.java
new file mode 100644
index 00000000000..ab4c9e90438
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/SubmissionPublisherTest.java
@@ -0,0 +1,1010 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea and Martin Buchholz with assistance from
+ * members of JCP JSR-166 Expert Group and released to the public
+ * domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Flow;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.SubmissionPublisher;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.BiPredicate;
+import java.util.stream.Stream;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import static java.util.concurrent.Flow.Publisher;
+import static java.util.concurrent.Flow.Subscriber;
+import static java.util.concurrent.Flow.Subscription;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+public class SubmissionPublisherTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(SubmissionPublisherTest.class);
+    }
+
+    final Executor basicExecutor = basicPublisher().getExecutor();
+
+    static SubmissionPublisher<Integer> basicPublisher() {
+        return new SubmissionPublisher<Integer>();
+    }
+
+    static class SPException extends RuntimeException {}
+
+    class TestSubscriber implements Subscriber<Integer> {
+        volatile Subscription sn;
+        int last;  // Requires that onNexts are in numeric order
+        volatile int nexts;
+        volatile int errors;
+        volatile int completes;
+        volatile boolean throwOnCall = false;
+        volatile boolean request = true;
+        volatile Throwable lastError;
+
+        public synchronized void onSubscribe(Subscription s) {
+            threadAssertTrue(sn == null);
+            sn = s;
+            notifyAll();
+            if (throwOnCall)
+                throw new SPException();
+            if (request)
+                sn.request(1L);
+        }
+        public synchronized void onNext(Integer t) {
+            ++nexts;
+            notifyAll();
+            int current = t.intValue();
+            threadAssertTrue(current >= last);
+            last = current;
+            if (request)
+                sn.request(1L);
+            if (throwOnCall)
+                throw new SPException();
+        }
+        public synchronized void onError(Throwable t) {
+            threadAssertTrue(completes == 0);
+            threadAssertTrue(errors == 0);
+            lastError = t;
+            ++errors;
+            notifyAll();
+        }
+        public synchronized void onComplete() {
+            threadAssertTrue(completes == 0);
+            ++completes;
+            notifyAll();
+        }
+
+        synchronized void awaitSubscribe() {
+            while (sn == null) {
+                try {
+                    wait();
+                } catch (Exception ex) {
+                    threadUnexpectedException(ex);
+                    break;
+                }
+            }
+        }
+        synchronized void awaitNext(int n) {
+            while (nexts < n) {
+                try {
+                    wait();
+                } catch (Exception ex) {
+                    threadUnexpectedException(ex);
+                    break;
+                }
+            }
+        }
+        synchronized void awaitComplete() {
+            while (completes == 0 && errors == 0) {
+                try {
+                    wait();
+                } catch (Exception ex) {
+                    threadUnexpectedException(ex);
+                    break;
+                }
+            }
+        }
+        synchronized void awaitError() {
+            while (errors == 0) {
+                try {
+                    wait();
+                } catch (Exception ex) {
+                    threadUnexpectedException(ex);
+                    break;
+                }
+            }
+        }
+
+    }
+
+    /**
+     * A new SubmissionPublisher has no subscribers, a non-null
+     * executor, a power-of-two capacity, is not closed, and reports
+     * zero demand and lag
+     */
+    void checkInitialState(SubmissionPublisher<?> p) {
+        assertFalse(p.hasSubscribers());
+        assertEquals(0, p.getNumberOfSubscribers());
+        assertTrue(p.getSubscribers().isEmpty());
+        assertFalse(p.isClosed());
+        assertNull(p.getClosedException());
+        int n = p.getMaxBufferCapacity();
+        assertTrue((n & (n - 1)) == 0); // power of two
+        assertNotNull(p.getExecutor());
+        assertEquals(0, p.estimateMinimumDemand());
+        assertEquals(0, p.estimateMaximumLag());
+    }
+
+    /**
+     * A default-constructed SubmissionPublisher has no subscribers,
+     * is not closed, has default buffer size, and uses the
+     * defaultExecutor
+     */
+    public void testConstructor1() {
+        SubmissionPublisher<Integer> p = new SubmissionPublisher<Integer>();
+        checkInitialState(p);
+        assertEquals(p.getMaxBufferCapacity(), Flow.defaultBufferSize());
+        Executor e = p.getExecutor(), c = ForkJoinPool.commonPool();
+        if (ForkJoinPool.getCommonPoolParallelism() > 1)
+            assertSame(e, c);
+        else
+            assertNotSame(e, c);
+    }
+
+    /**
+     * A new SubmissionPublisher has no subscribers, is not closed,
+     * has the given buffer size, and uses the given executor
+     */
+    public void testConstructor2() {
+        Executor e = Executors.newFixedThreadPool(1);
+        SubmissionPublisher<Integer> p = new SubmissionPublisher<Integer>(e, 8);
+        checkInitialState(p);
+        assertSame(p.getExecutor(), e);
+        assertEquals(8, p.getMaxBufferCapacity());
+    }
+
+    /**
+     * A null Executor argument to SubmissionPublisher constructor throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            new SubmissionPublisher<Integer>(null, 8);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * A negative capacity argument to SubmissionPublisher constructor
+     * throws IAE
+     */
+    public void testConstructor4() {
+        Executor e = Executors.newFixedThreadPool(1);
+        try {
+            new SubmissionPublisher<Integer>(e, -1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * A closed publisher reports isClosed with no closedException and
+     * throws ISE upon attempted submission; a subsequent close or
+     * closeExceptionally has no additional effect.
+     */
+    public void testClose() {
+        SubmissionPublisher<Integer> p = basicPublisher();
+        checkInitialState(p);
+        p.close();
+        assertTrue(p.isClosed());
+        assertNull(p.getClosedException());
+        try {
+            p.submit(1);
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+        Throwable ex = new SPException();
+        p.closeExceptionally(ex);
+        assertTrue(p.isClosed());
+        assertNull(p.getClosedException());
+    }
+
+    /**
+     * A publisher closedExceptionally reports isClosed with the
+     * closedException and throws ISE upon attempted submission; a
+     * subsequent close or closeExceptionally has no additional
+     * effect.
+     */
+    public void testCloseExceptionally() {
+        SubmissionPublisher<Integer> p = basicPublisher();
+        checkInitialState(p);
+        Throwable ex = new SPException();
+        p.closeExceptionally(ex);
+        assertTrue(p.isClosed());
+        assertSame(p.getClosedException(), ex);
+        try {
+            p.submit(1);
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+        p.close();
+        assertTrue(p.isClosed());
+        assertSame(p.getClosedException(), ex);
+    }
+
+    /**
+     * Upon subscription, the subscriber's onSubscribe is called, no
+     * other Subscriber methods are invoked, the publisher
+     * hasSubscribers, isSubscribed is true, and existing
+     * subscriptions are unaffected.
+     */
+    public void testSubscribe1() {
+        TestSubscriber s = new TestSubscriber();
+        SubmissionPublisher<Integer> p = basicPublisher();
+        p.subscribe(s);
+        assertTrue(p.hasSubscribers());
+        assertEquals(1, p.getNumberOfSubscribers());
+        assertTrue(p.getSubscribers().contains(s));
+        assertTrue(p.isSubscribed(s));
+        s.awaitSubscribe();
+        assertNotNull(s.sn);
+        assertEquals(0, s.nexts);
+        assertEquals(0, s.errors);
+        assertEquals(0, s.completes);
+        TestSubscriber s2 = new TestSubscriber();
+        p.subscribe(s2);
+        assertTrue(p.hasSubscribers());
+        assertEquals(2, p.getNumberOfSubscribers());
+        assertTrue(p.getSubscribers().contains(s));
+        assertTrue(p.getSubscribers().contains(s2));
+        assertTrue(p.isSubscribed(s));
+        assertTrue(p.isSubscribed(s2));
+        s2.awaitSubscribe();
+        assertNotNull(s2.sn);
+        assertEquals(0, s2.nexts);
+        assertEquals(0, s2.errors);
+        assertEquals(0, s2.completes);
+        p.close();
+    }
+
+    /**
+     * If closed, upon subscription, the subscriber's onComplete
+     * method is invoked
+     */
+    public void testSubscribe2() {
+        TestSubscriber s = new TestSubscriber();
+        SubmissionPublisher<Integer> p = basicPublisher();
+        p.close();
+        p.subscribe(s);
+        s.awaitComplete();
+        assertEquals(0, s.nexts);
+        assertEquals(0, s.errors);
+        assertEquals(1, s.completes, 1);
+    }
+
+    /**
+     * If closedExceptionally, upon subscription, the subscriber's
+     * onError method is invoked
+     */
+    public void testSubscribe3() {
+        TestSubscriber s = new TestSubscriber();
+        SubmissionPublisher<Integer> p = basicPublisher();
+        Throwable ex = new SPException();
+        p.closeExceptionally(ex);
+        assertTrue(p.isClosed());
+        assertSame(p.getClosedException(), ex);
+        p.subscribe(s);
+        s.awaitError();
+        assertEquals(0, s.nexts);
+        assertEquals(1, s.errors);
+    }
+
+    /**
+     * Upon attempted resubscription, the subscriber's onError is
+     * called and the subscription is cancelled.
+     */
+    public void testSubscribe4() {
+        TestSubscriber s = new TestSubscriber();
+        SubmissionPublisher<Integer> p = basicPublisher();
+        p.subscribe(s);
+        assertTrue(p.hasSubscribers());
+        assertEquals(1, p.getNumberOfSubscribers());
+        assertTrue(p.getSubscribers().contains(s));
+        assertTrue(p.isSubscribed(s));
+        s.awaitSubscribe();
+        assertNotNull(s.sn);
+        assertEquals(0, s.nexts);
+        assertEquals(0, s.errors);
+        assertEquals(0, s.completes);
+        p.subscribe(s);
+        s.awaitError();
+        assertEquals(0, s.nexts);
+        assertEquals(1, s.errors);
+        assertFalse(p.isSubscribed(s));
+    }
+
+    /**
+     * An exception thrown in onSubscribe causes onError
+     */
+    public void testSubscribe5() {
+        TestSubscriber s = new TestSubscriber();
+        SubmissionPublisher<Integer> p = basicPublisher();
+        s.throwOnCall = true;
+        try {
+            p.subscribe(s);
+        } catch (Exception ok) {}
+        s.awaitError();
+        assertEquals(0, s.nexts);
+        assertEquals(1, s.errors);
+        assertEquals(0, s.completes);
+    }
+
+    /**
+     * subscribe(null) throws NPE
+     */
+    public void testSubscribe6() {
+        SubmissionPublisher<Integer> p = basicPublisher();
+        try {
+            p.subscribe(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+        checkInitialState(p);
+    }
+
+    /**
+     * Closing a publisher causes onComplete to subscribers
+     */
+    public void testCloseCompletes() {
+        SubmissionPublisher<Integer> p = basicPublisher();
+        TestSubscriber s1 = new TestSubscriber();
+        TestSubscriber s2 = new TestSubscriber();
+        p.subscribe(s1);
+        p.subscribe(s2);
+        p.submit(1);
+        p.close();
+        assertTrue(p.isClosed());
+        assertNull(p.getClosedException());
+        s1.awaitComplete();
+        assertEquals(1, s1.nexts);
+        assertEquals(1, s1.completes);
+        s2.awaitComplete();
+        assertEquals(1, s2.nexts);
+        assertEquals(1, s2.completes);
+    }
+
+    /**
+     * Closing a publisher exceptionally causes onError to subscribers
+     */
+    public void testCloseExceptionallyError() {
+        SubmissionPublisher<Integer> p = basicPublisher();
+        TestSubscriber s1 = new TestSubscriber();
+        TestSubscriber s2 = new TestSubscriber();
+        p.subscribe(s1);
+        p.subscribe(s2);
+        p.submit(1);
+        p.closeExceptionally(new SPException());
+        assertTrue(p.isClosed());
+        s1.awaitError();
+        assertTrue(s1.nexts <= 1);
+        assertEquals(1, s1.errors);
+        s2.awaitError();
+        assertTrue(s2.nexts <= 1);
+        assertEquals(1, s2.errors);
+    }
+
+    /**
+     * Cancelling a subscription eventually causes no more onNexts to be issued
+     */
+    public void testCancel() {
+        SubmissionPublisher<Integer> p = basicPublisher();
+        TestSubscriber s1 = new TestSubscriber();
+        TestSubscriber s2 = new TestSubscriber();
+        p.subscribe(s1);
+        p.subscribe(s2);
+        s1.awaitSubscribe();
+        p.submit(1);
+        s1.sn.cancel();
+        for (int i = 2; i <= 20; ++i)
+            p.submit(i);
+        p.close();
+        s2.awaitComplete();
+        assertEquals(20, s2.nexts);
+        assertEquals(1, s2.completes);
+        assertTrue(s1.nexts < 20);
+        assertFalse(p.isSubscribed(s1));
+    }
+
+    /**
+     * Throwing an exception in onNext causes onError
+     */
+    public void testThrowOnNext() {
+        SubmissionPublisher<Integer> p = basicPublisher();
+        TestSubscriber s1 = new TestSubscriber();
+        TestSubscriber s2 = new TestSubscriber();
+        p.subscribe(s1);
+        p.subscribe(s2);
+        s1.awaitSubscribe();
+        p.submit(1);
+        s1.throwOnCall = true;
+        p.submit(2);
+        p.close();
+        s2.awaitComplete();
+        assertEquals(2, s2.nexts);
+        s1.awaitComplete();
+        assertEquals(1, s1.errors);
+    }
+
+    /**
+     * If a handler is supplied in constructor, it is invoked when
+     * subscriber throws an exception in onNext
+     */
+    public void testThrowOnNextHandler() {
+        AtomicInteger calls = new AtomicInteger();
+        SubmissionPublisher<Integer> p = new SubmissionPublisher<Integer>
+            (basicExecutor, 8,
+             (s, e) -> calls.getAndIncrement());
+        TestSubscriber s1 = new TestSubscriber();
+        TestSubscriber s2 = new TestSubscriber();
+        p.subscribe(s1);
+        p.subscribe(s2);
+        s1.awaitSubscribe();
+        p.submit(1);
+        s1.throwOnCall = true;
+        p.submit(2);
+        p.close();
+        s2.awaitComplete();
+        assertEquals(2, s2.nexts);
+        assertEquals(1, s2.completes);
+        s1.awaitError();
+        assertEquals(1, s1.errors);
+        assertEquals(1, calls.get());
+    }
+
+    /**
+     * onNext items are issued in the same order to each subscriber
+     */
+    public void testOrder() {
+        SubmissionPublisher<Integer> p = basicPublisher();
+        TestSubscriber s1 = new TestSubscriber();
+        TestSubscriber s2 = new TestSubscriber();
+        p.subscribe(s1);
+        p.subscribe(s2);
+        for (int i = 1; i <= 20; ++i)
+            p.submit(i);
+        p.close();
+        s2.awaitComplete();
+        s1.awaitComplete();
+        assertEquals(20, s2.nexts);
+        assertEquals(1, s2.completes);
+        assertEquals(20, s1.nexts);
+        assertEquals(1, s1.completes);
+    }
+
+    /**
+     * onNext is issued only if requested
+     */
+    public void testRequest1() {
+        SubmissionPublisher<Integer> p = basicPublisher();
+        TestSubscriber s1 = new TestSubscriber();
+        s1.request = false;
+        p.subscribe(s1);
+        s1.awaitSubscribe();
+        assertTrue(p.estimateMinimumDemand() == 0);
+        TestSubscriber s2 = new TestSubscriber();
+        p.subscribe(s2);
+        p.submit(1);
+        p.submit(2);
+        s2.awaitNext(1);
+        assertEquals(0, s1.nexts);
+        s1.sn.request(3);
+        p.submit(3);
+        p.close();
+        s2.awaitComplete();
+        assertEquals(3, s2.nexts);
+        assertEquals(1, s2.completes);
+        s1.awaitComplete();
+        assertTrue(s1.nexts > 0);
+        assertEquals(1, s1.completes);
+    }
+
+    /**
+     * onNext is not issued when requests become zero
+     */
+    public void testRequest2() {
+        SubmissionPublisher<Integer> p = basicPublisher();
+        TestSubscriber s1 = new TestSubscriber();
+        TestSubscriber s2 = new TestSubscriber();
+        p.subscribe(s1);
+        p.subscribe(s2);
+        s2.awaitSubscribe();
+        s1.awaitSubscribe();
+        s1.request = false;
+        p.submit(1);
+        p.submit(2);
+        p.close();
+        s2.awaitComplete();
+        assertEquals(2, s2.nexts);
+        assertEquals(1, s2.completes);
+        s1.awaitNext(1);
+        assertEquals(1, s1.nexts);
+    }
+
+    /**
+     * Negative request causes error
+     */
+    public void testRequest3() {
+        SubmissionPublisher<Integer> p = basicPublisher();
+        TestSubscriber s1 = new TestSubscriber();
+        TestSubscriber s2 = new TestSubscriber();
+        p.subscribe(s1);
+        p.subscribe(s2);
+        s2.awaitSubscribe();
+        s1.awaitSubscribe();
+        s1.sn.request(-1L);
+        p.submit(1);
+        p.submit(2);
+        p.close();
+        s2.awaitComplete();
+        assertEquals(2, s2.nexts);
+        assertEquals(1, s2.completes);
+        s1.awaitError();
+        assertEquals(1, s1.errors);
+        assertTrue(s1.lastError instanceof IllegalArgumentException);
+    }
+
+    /**
+     * estimateMinimumDemand reports 0 until request, nonzero after
+     * request, and zero again after delivery
+     */
+    public void testEstimateMinimumDemand() {
+        TestSubscriber s = new TestSubscriber();
+        SubmissionPublisher<Integer> p = basicPublisher();
+        s.request = false;
+        p.subscribe(s);
+        s.awaitSubscribe();
+        assertEquals(0, p.estimateMinimumDemand());
+        s.sn.request(1);
+        assertEquals(1, p.estimateMinimumDemand());
+        p.submit(1);
+        s.awaitNext(1);
+        assertEquals(0, p.estimateMinimumDemand());
+    }
+
+    /**
+     * submit to a publisher with no subscribers returns lag 0
+     */
+    public void testEmptySubmit() {
+        SubmissionPublisher<Integer> p = basicPublisher();
+        assertEquals(0, p.submit(1));
+    }
+
+    /**
+     * submit(null) throws NPE
+     */
+    public void testNullSubmit() {
+        SubmissionPublisher<Integer> p = basicPublisher();
+        try {
+            p.submit(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * submit returns number of lagged items, compatible with result
+     * of estimateMaximumLag.
+     */
+    public void testLaggedSubmit() {
+        SubmissionPublisher<Integer> p = basicPublisher();
+        TestSubscriber s1 = new TestSubscriber();
+        s1.request = false;
+        TestSubscriber s2 = new TestSubscriber();
+        s2.request = false;
+        p.subscribe(s1);
+        p.subscribe(s2);
+        s2.awaitSubscribe();
+        s1.awaitSubscribe();
+        assertEquals(1, p.submit(1));
+        assertTrue(p.estimateMaximumLag() >= 1);
+        assertTrue(p.submit(2) >= 2);
+        assertTrue(p.estimateMaximumLag() >= 2);
+        s1.sn.request(4);
+        assertTrue(p.submit(3) >= 3);
+        assertTrue(p.estimateMaximumLag() >= 3);
+        s2.sn.request(4);
+        p.submit(4);
+        p.close();
+        s2.awaitComplete();
+        assertEquals(4, s2.nexts);
+        s1.awaitComplete();
+        assertEquals(4, s2.nexts);
+    }
+
+    /**
+     * submit eventually issues requested items when buffer capacity is 1
+     */
+    public void testCap1Submit() {
+        SubmissionPublisher<Integer> p = new SubmissionPublisher<Integer>(
+            basicExecutor, 1);
+        TestSubscriber s1 = new TestSubscriber();
+        TestSubscriber s2 = new TestSubscriber();
+        p.subscribe(s1);
+        p.subscribe(s2);
+        for (int i = 1; i <= 20; ++i) {
+            assertTrue(p.estimateMinimumDemand() <= 1);
+            assertTrue(p.submit(i) >= 0);
+        }
+        p.close();
+        s2.awaitComplete();
+        s1.awaitComplete();
+        assertEquals(20, s2.nexts);
+        assertEquals(1, s2.completes);
+        assertEquals(20, s1.nexts);
+        assertEquals(1, s1.completes);
+    }
+
+    static boolean noopHandle(AtomicInteger count) {
+        count.getAndIncrement();
+        return false;
+    }
+
+    static boolean reqHandle(AtomicInteger count, Subscriber s) {
+        count.getAndIncrement();
+        ((TestSubscriber)s).sn.request(Long.MAX_VALUE);
+        return true;
+    }
+
+    /**
+     * offer to a publisher with no subscribers returns lag 0
+     */
+    public void testEmptyOffer() {
+        SubmissionPublisher<Integer> p = basicPublisher();
+        assertEquals(0, p.offer(1, null));
+    }
+
+    /**
+     * offer(null) throws NPE
+     */
+    public void testNullOffer() {
+        SubmissionPublisher<Integer> p = basicPublisher();
+        try {
+            p.offer(null, null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * offer returns number of lagged items if not saturated
+     */
+    public void testLaggedOffer() {
+        SubmissionPublisher<Integer> p = basicPublisher();
+        TestSubscriber s1 = new TestSubscriber();
+        s1.request = false;
+        TestSubscriber s2 = new TestSubscriber();
+        s2.request = false;
+        p.subscribe(s1);
+        p.subscribe(s2);
+        s2.awaitSubscribe();
+        s1.awaitSubscribe();
+        assertTrue(p.offer(1, null) >= 1);
+        assertTrue(p.offer(2, null) >= 2);
+        s1.sn.request(4);
+        assertTrue(p.offer(3, null) >= 3);
+        s2.sn.request(4);
+        p.offer(4, null);
+        p.close();
+        s2.awaitComplete();
+        assertEquals(4, s2.nexts);
+        s1.awaitComplete();
+        assertEquals(4, s2.nexts);
+    }
+
+    /**
+     * offer reports drops if saturated
+     */
+    public void testDroppedOffer() {
+        SubmissionPublisher<Integer> p = new SubmissionPublisher<Integer>(
+            basicExecutor, 4);
+        TestSubscriber s1 = new TestSubscriber();
+        s1.request = false;
+        TestSubscriber s2 = new TestSubscriber();
+        s2.request = false;
+        p.subscribe(s1);
+        p.subscribe(s2);
+        s2.awaitSubscribe();
+        s1.awaitSubscribe();
+        for (int i = 1; i <= 4; ++i)
+            assertTrue(p.offer(i, null) >= 0);
+        p.offer(5, null);
+        assertTrue(p.offer(6, null) < 0);
+        s1.sn.request(64);
+        assertTrue(p.offer(7, null) < 0);
+        s2.sn.request(64);
+        p.close();
+        s2.awaitComplete();
+        assertTrue(s2.nexts >= 4);
+        s1.awaitComplete();
+        assertTrue(s1.nexts >= 4);
+    }
+
+    /**
+     * offer invokes drop handler if saturated
+     */
+    public void testHandledDroppedOffer() {
+        AtomicInteger calls = new AtomicInteger();
+        SubmissionPublisher<Integer> p = new SubmissionPublisher<Integer>(
+            basicExecutor, 4);
+        TestSubscriber s1 = new TestSubscriber();
+        s1.request = false;
+        TestSubscriber s2 = new TestSubscriber();
+        s2.request = false;
+        p.subscribe(s1);
+        p.subscribe(s2);
+        s2.awaitSubscribe();
+        s1.awaitSubscribe();
+        for (int i = 1; i <= 4; ++i)
+            assertTrue(p.offer(i, (s, x) -> noopHandle(calls)) >= 0);
+        p.offer(4, (s, x) -> noopHandle(calls));
+        assertTrue(p.offer(6, (s, x) -> noopHandle(calls)) < 0);
+        s1.sn.request(64);
+        assertTrue(p.offer(7, (s, x) -> noopHandle(calls)) < 0);
+        s2.sn.request(64);
+        p.close();
+        s2.awaitComplete();
+        s1.awaitComplete();
+        assertTrue(calls.get() >= 4);
+    }
+
+    /**
+     * offer succeeds if drop handler forces request
+     */
+    public void testRecoveredHandledDroppedOffer() {
+        AtomicInteger calls = new AtomicInteger();
+        SubmissionPublisher<Integer> p = new SubmissionPublisher<Integer>(
+            basicExecutor, 4);
+        TestSubscriber s1 = new TestSubscriber();
+        s1.request = false;
+        TestSubscriber s2 = new TestSubscriber();
+        s2.request = false;
+        p.subscribe(s1);
+        p.subscribe(s2);
+        s2.awaitSubscribe();
+        s1.awaitSubscribe();
+        int n = 0;
+        for (int i = 1; i <= 8; ++i) {
+            int d = p.offer(i, (s, x) -> reqHandle(calls, s));
+            n = n + 2 + (d < 0 ? d : 0);
+        }
+        p.close();
+        s2.awaitComplete();
+        s1.awaitComplete();
+        assertEquals(n, s1.nexts + s2.nexts);
+        assertTrue(calls.get() >= 2);
+    }
+
+    /**
+     * Timed offer to a publisher with no subscribers returns lag 0
+     */
+    public void testEmptyTimedOffer() {
+        SubmissionPublisher<Integer> p = basicPublisher();
+        long startTime = System.nanoTime();
+        assertEquals(0, p.offer(1, LONG_DELAY_MS, MILLISECONDS, null));
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS / 2);
+    }
+
+    /**
+     * Timed offer with null item or TimeUnit throws NPE
+     */
+    public void testNullTimedOffer() {
+        SubmissionPublisher<Integer> p = basicPublisher();
+        long startTime = System.nanoTime();
+        try {
+            p.offer(null, LONG_DELAY_MS, MILLISECONDS, null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+        try {
+            p.offer(1, LONG_DELAY_MS, null, null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS / 2);
+    }
+
+    /**
+     * Timed offer returns number of lagged items if not saturated
+     */
+    public void testLaggedTimedOffer() {
+        SubmissionPublisher<Integer> p = basicPublisher();
+        TestSubscriber s1 = new TestSubscriber();
+        s1.request = false;
+        TestSubscriber s2 = new TestSubscriber();
+        s2.request = false;
+        p.subscribe(s1);
+        p.subscribe(s2);
+        s2.awaitSubscribe();
+        s1.awaitSubscribe();
+        long startTime = System.nanoTime();
+        assertTrue(p.offer(1, LONG_DELAY_MS, MILLISECONDS, null) >= 1);
+        assertTrue(p.offer(2, LONG_DELAY_MS, MILLISECONDS, null) >= 2);
+        s1.sn.request(4);
+        assertTrue(p.offer(3, LONG_DELAY_MS, MILLISECONDS, null) >= 3);
+        s2.sn.request(4);
+        p.offer(4, LONG_DELAY_MS, MILLISECONDS, null);
+        p.close();
+        s2.awaitComplete();
+        assertEquals(4, s2.nexts);
+        s1.awaitComplete();
+        assertEquals(4, s2.nexts);
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS / 2);
+    }
+
+    /**
+     * Timed offer reports drops if saturated
+     */
+    public void testDroppedTimedOffer() {
+        SubmissionPublisher<Integer> p = new SubmissionPublisher<Integer>(
+            basicExecutor, 4);
+        TestSubscriber s1 = new TestSubscriber();
+        s1.request = false;
+        TestSubscriber s2 = new TestSubscriber();
+        s2.request = false;
+        p.subscribe(s1);
+        p.subscribe(s2);
+        s2.awaitSubscribe();
+        s1.awaitSubscribe();
+        long delay = timeoutMillis();
+        for (int i = 1; i <= 4; ++i)
+            assertTrue(p.offer(i, delay, MILLISECONDS, null) >= 0);
+        long startTime = System.nanoTime();
+        assertTrue(p.offer(5, delay, MILLISECONDS, null) < 0);
+        s1.sn.request(64);
+        assertTrue(p.offer(6, delay, MILLISECONDS, null) < 0);
+        // 2 * delay should elapse but check only 1 * delay to allow timer slop
+        assertTrue(millisElapsedSince(startTime) >= delay);
+        s2.sn.request(64);
+        p.close();
+        s2.awaitComplete();
+        assertTrue(s2.nexts >= 2);
+        s1.awaitComplete();
+        assertTrue(s1.nexts >= 2);
+    }
+
+    /**
+     * Timed offer invokes drop handler if saturated
+     */
+    public void testHandledDroppedTimedOffer() {
+        AtomicInteger calls = new AtomicInteger();
+        SubmissionPublisher<Integer> p = new SubmissionPublisher<Integer>(
+            basicExecutor, 4);
+        TestSubscriber s1 = new TestSubscriber();
+        s1.request = false;
+        TestSubscriber s2 = new TestSubscriber();
+        s2.request = false;
+        p.subscribe(s1);
+        p.subscribe(s2);
+        s2.awaitSubscribe();
+        s1.awaitSubscribe();
+        long delay = timeoutMillis();
+        for (int i = 1; i <= 4; ++i)
+            assertTrue(p.offer(i, delay, MILLISECONDS, (s, x) -> noopHandle(calls)) >= 0);
+        long startTime = System.nanoTime();
+        assertTrue(p.offer(5, delay, MILLISECONDS, (s, x) -> noopHandle(calls)) < 0);
+        s1.sn.request(64);
+        assertTrue(p.offer(6, delay, MILLISECONDS, (s, x) -> noopHandle(calls)) < 0);
+        assertTrue(millisElapsedSince(startTime) >= delay);
+        s2.sn.request(64);
+        p.close();
+        s2.awaitComplete();
+        s1.awaitComplete();
+        assertTrue(calls.get() >= 2);
+    }
+
+    /**
+     * Timed offer succeeds if drop handler forces request
+     */
+    public void testRecoveredHandledDroppedTimedOffer() {
+        AtomicInteger calls = new AtomicInteger();
+        SubmissionPublisher<Integer> p = new SubmissionPublisher<Integer>(
+            basicExecutor, 4);
+        TestSubscriber s1 = new TestSubscriber();
+        s1.request = false;
+        TestSubscriber s2 = new TestSubscriber();
+        s2.request = false;
+        p.subscribe(s1);
+        p.subscribe(s2);
+        s2.awaitSubscribe();
+        s1.awaitSubscribe();
+        int n = 0;
+        long delay = timeoutMillis();
+        long startTime = System.nanoTime();
+        for (int i = 1; i <= 6; ++i) {
+            int d = p.offer(i, delay, MILLISECONDS, (s, x) -> reqHandle(calls, s));
+            n = n + 2 + (d < 0 ? d : 0);
+        }
+        assertTrue(millisElapsedSince(startTime) >= delay);
+        p.close();
+        s2.awaitComplete();
+        s1.awaitComplete();
+        assertEquals(n, s1.nexts + s2.nexts);
+        assertTrue(calls.get() >= 2);
+    }
+
+    /**
+     * consume returns a CompletableFuture that is done when
+     * publisher completes
+     */
+    public void testConsume() {
+        AtomicInteger sum = new AtomicInteger();
+        SubmissionPublisher<Integer> p = basicPublisher();
+        CompletableFuture<Void> f =
+            p.consume((Integer x) -> { sum.getAndAdd(x.intValue()); });
+        int n = 20;
+        for (int i = 1; i <= n; ++i)
+            p.submit(i);
+        p.close();
+        f.join();
+        assertEquals((n * (n + 1)) / 2, sum.get());
+    }
+
+    /**
+     * consume(null) throws NPE
+     */
+    public void testConsumeNPE() {
+        SubmissionPublisher<Integer> p = basicPublisher();
+        try {
+            CompletableFuture<Void> f = p.consume(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * consume eventually stops processing published items if cancelled
+     */
+    public void testCancelledConsume() {
+        AtomicInteger count = new AtomicInteger();
+        SubmissionPublisher<Integer> p = basicPublisher();
+        CompletableFuture<Void> f = p.consume(x -> count.getAndIncrement());
+        f.cancel(true);
+        int n = 1000000; // arbitrary limit
+        for (int i = 1; i <= n; ++i)
+            p.submit(i);
+        assertTrue(count.get() < n);
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/SynchronousQueueTest.java b/jdk/test/java/util/concurrent/tck/SynchronousQueueTest.java
new file mode 100644
index 00000000000..b4529801928
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/SynchronousQueueTest.java
@@ -0,0 +1,643 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.SynchronousQueue;
+
+import junit.framework.Test;
+
+public class SynchronousQueueTest extends JSR166TestCase {
+
+    public static class Fair extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new SynchronousQueue(true);
+        }
+    }
+
+    public static class NonFair extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new SynchronousQueue(false);
+        }
+    }
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return newTestSuite(SynchronousQueueTest.class,
+                            new Fair().testSuite(),
+                            new NonFair().testSuite());
+    }
+
+    /**
+     * Any SynchronousQueue is both empty and full
+     */
+    public void testEmptyFull()      { testEmptyFull(false); }
+    public void testEmptyFull_fair() { testEmptyFull(true); }
+    public void testEmptyFull(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        assertEquals(0, q.remainingCapacity());
+        assertFalse(q.offer(zero));
+    }
+
+    /**
+     * offer fails if no active taker
+     */
+    public void testOffer()      { testOffer(false); }
+    public void testOffer_fair() { testOffer(true); }
+    public void testOffer(boolean fair) {
+        SynchronousQueue q = new SynchronousQueue(fair);
+        assertFalse(q.offer(one));
+    }
+
+    /**
+     * add throws IllegalStateException if no active taker
+     */
+    public void testAdd()      { testAdd(false); }
+    public void testAdd_fair() { testAdd(true); }
+    public void testAdd(boolean fair) {
+        SynchronousQueue q = new SynchronousQueue(fair);
+        assertEquals(0, q.remainingCapacity());
+        try {
+            q.add(one);
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * addAll(this) throws IllegalArgumentException
+     */
+    public void testAddAll_self()      { testAddAll_self(false); }
+    public void testAddAll_self_fair() { testAddAll_self(true); }
+    public void testAddAll_self(boolean fair) {
+        SynchronousQueue q = new SynchronousQueue(fair);
+        try {
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll throws ISE if no active taker
+     */
+    public void testAddAll_ISE()      { testAddAll_ISE(false); }
+    public void testAddAll_ISE_fair() { testAddAll_ISE(true); }
+    public void testAddAll_ISE(boolean fair) {
+        SynchronousQueue q = new SynchronousQueue(fair);
+        Integer[] ints = new Integer[1];
+        for (int i = 0; i < ints.length; i++)
+            ints[i] = i;
+        Collection<Integer> coll = Arrays.asList(ints);
+        try {
+            q.addAll(coll);
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * put blocks interruptibly if no active taker
+     */
+    public void testBlockingPut()      { testBlockingPut(false); }
+    public void testBlockingPut_fair() { testBlockingPut(true); }
+    public void testBlockingPut(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                Thread.currentThread().interrupt();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * put blocks interruptibly waiting for take
+     */
+    public void testPutWithTake()      { testPutWithTake(false); }
+    public void testPutWithTake_fair() { testPutWithTake(true); }
+    public void testPutWithTake(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        final CountDownLatch pleaseTake = new CountDownLatch(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                pleaseTake.countDown();
+                q.put(one);
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseTake);
+        assertEquals(0, q.remainingCapacity());
+        try { assertSame(one, q.take()); }
+        catch (InterruptedException e) { threadUnexpectedException(e); }
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * timed offer times out if elements not taken
+     */
+    public void testTimedOffer()      { testTimedOffer(false); }
+    public void testTimedOffer_fair() { testTimedOffer(true); }
+    public void testTimedOffer(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                assertFalse(q.offer(new Object(), timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                pleaseInterrupt.countDown();
+                try {
+                    q.offer(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * poll return null if no active putter
+     */
+    public void testPoll()      { testPoll(false); }
+    public void testPoll_fair() { testPoll(true); }
+    public void testPoll(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        assertNull(q.poll());
+    }
+
+    /**
+     * timed poll with zero timeout times out if no active putter
+     */
+    public void testTimedPoll0()      { testTimedPoll0(false); }
+    public void testTimedPoll0_fair() { testTimedPoll0(true); }
+    public void testTimedPoll0(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        try { assertNull(q.poll(0, MILLISECONDS)); }
+        catch (InterruptedException e) { threadUnexpectedException(e); }
+    }
+
+    /**
+     * timed poll with nonzero timeout times out if no active putter
+     */
+    public void testTimedPoll()      { testTimedPoll(false); }
+    public void testTimedPoll_fair() { testTimedPoll(true); }
+    public void testTimedPoll(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        long startTime = System.nanoTime();
+        try { assertNull(q.poll(timeoutMillis(), MILLISECONDS)); }
+        catch (InterruptedException e) { threadUnexpectedException(e); }
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+    }
+
+    /**
+     * timed poll before a delayed offer times out, returning null;
+     * after offer succeeds; on interruption throws
+     */
+    public void testTimedPollWithOffer()      { testTimedPollWithOffer(false); }
+    public void testTimedPollWithOffer_fair() { testTimedPollWithOffer(true); }
+    public void testTimedPollWithOffer(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        final CountDownLatch pleaseOffer = new CountDownLatch(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+
+                pleaseOffer.countDown();
+                startTime = System.nanoTime();
+                assertSame(zero, q.poll(LONG_DELAY_MS, MILLISECONDS));
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+            }});
+
+        await(pleaseOffer);
+        long startTime = System.nanoTime();
+        try { assertTrue(q.offer(zero, LONG_DELAY_MS, MILLISECONDS)); }
+        catch (InterruptedException e) { threadUnexpectedException(e); }
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * peek() returns null if no active putter
+     */
+    public void testPeek()      { testPeek(false); }
+    public void testPeek_fair() { testPeek(true); }
+    public void testPeek(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        assertNull(q.peek());
+    }
+
+    /**
+     * element() throws NoSuchElementException if no active putter
+     */
+    public void testElement()      { testElement(false); }
+    public void testElement_fair() { testElement(true); }
+    public void testElement(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove() throws NoSuchElementException if no active putter
+     */
+    public void testRemove()      { testRemove(false); }
+    public void testRemove_fair() { testRemove(true); }
+    public void testRemove(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * contains returns false
+     */
+    public void testContains()      { testContains(false); }
+    public void testContains_fair() { testContains(true); }
+    public void testContains(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        assertFalse(q.contains(zero));
+    }
+
+    /**
+     * clear ensures isEmpty
+     */
+    public void testClear()      { testClear(false); }
+    public void testClear_fair() { testClear(true); }
+    public void testClear(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll returns false unless empty
+     */
+    public void testContainsAll()      { testContainsAll(false); }
+    public void testContainsAll_fair() { testContainsAll(true); }
+    public void testContainsAll(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        Integer[] empty = new Integer[0];
+        assertTrue(q.containsAll(Arrays.asList(empty)));
+        Integer[] ints = new Integer[1]; ints[0] = zero;
+        assertFalse(q.containsAll(Arrays.asList(ints)));
+    }
+
+    /**
+     * retainAll returns false
+     */
+    public void testRetainAll()      { testRetainAll(false); }
+    public void testRetainAll_fair() { testRetainAll(true); }
+    public void testRetainAll(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        Integer[] empty = new Integer[0];
+        assertFalse(q.retainAll(Arrays.asList(empty)));
+        Integer[] ints = new Integer[1]; ints[0] = zero;
+        assertFalse(q.retainAll(Arrays.asList(ints)));
+    }
+
+    /**
+     * removeAll returns false
+     */
+    public void testRemoveAll()      { testRemoveAll(false); }
+    public void testRemoveAll_fair() { testRemoveAll(true); }
+    public void testRemoveAll(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        Integer[] empty = new Integer[0];
+        assertFalse(q.removeAll(Arrays.asList(empty)));
+        Integer[] ints = new Integer[1]; ints[0] = zero;
+        assertFalse(q.containsAll(Arrays.asList(ints)));
+    }
+
+    /**
+     * toArray is empty
+     */
+    public void testToArray()      { testToArray(false); }
+    public void testToArray_fair() { testToArray(true); }
+    public void testToArray(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        Object[] o = q.toArray();
+        assertEquals(0, o.length);
+    }
+
+    /**
+     * toArray(Integer array) returns its argument with the first
+     * element (if present) nulled out
+     */
+    public void testToArray2()      { testToArray2(false); }
+    public void testToArray2_fair() { testToArray2(true); }
+    public void testToArray2(boolean fair) {
+        final SynchronousQueue<Integer> q
+            = new SynchronousQueue<Integer>(fair);
+        Integer[] a;
+
+        a = new Integer[0];
+        assertSame(a, q.toArray(a));
+
+        a = new Integer[3];
+        Arrays.fill(a, 42);
+        assertSame(a, q.toArray(a));
+        assertNull(a[0]);
+        for (int i = 1; i < a.length; i++)
+            assertEquals(42, (int) a[i]);
+    }
+
+    /**
+     * toArray(null) throws NPE
+     */
+    public void testToArray_null()      { testToArray_null(false); }
+    public void testToArray_null_fair() { testToArray_null(true); }
+    public void testToArray_null(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        try {
+            Object[] o = q.toArray(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * iterator does not traverse any elements
+     */
+    public void testIterator()      { testIterator(false); }
+    public void testIterator_fair() { testIterator(true); }
+    public void testIterator(boolean fair) {
+        assertIteratorExhausted(new SynchronousQueue(fair).iterator());
+    }
+
+    /**
+     * iterator remove throws ISE
+     */
+    public void testIteratorRemove()      { testIteratorRemove(false); }
+    public void testIteratorRemove_fair() { testIteratorRemove(true); }
+    public void testIteratorRemove(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        Iterator it = q.iterator();
+        try {
+            it.remove();
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * toString returns a non-null string
+     */
+    public void testToString()      { testToString(false); }
+    public void testToString_fair() { testToString(true); }
+    public void testToString(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        String s = q.toString();
+        assertNotNull(s);
+    }
+
+    /**
+     * offer transfers elements across Executor tasks
+     */
+    public void testOfferInExecutor()      { testOfferInExecutor(false); }
+    public void testOfferInExecutor_fair() { testOfferInExecutor(true); }
+    public void testOfferInExecutor(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        final ExecutorService executor = Executors.newFixedThreadPool(2);
+        try (PoolCleaner cleaner = cleaner(executor)) {
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(q.offer(one));
+                    threadsStarted.await();
+                    assertTrue(q.offer(one, LONG_DELAY_MS, MILLISECONDS));
+                    assertEquals(0, q.remainingCapacity());
+                }});
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadsStarted.await();
+                    assertSame(one, q.take());
+                }});
+        }
+    }
+
+    /**
+     * timed poll retrieves elements across Executor threads
+     */
+    public void testPollInExecutor()      { testPollInExecutor(false); }
+    public void testPollInExecutor_fair() { testPollInExecutor(true); }
+    public void testPollInExecutor(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        final ExecutorService executor = Executors.newFixedThreadPool(2);
+        try (PoolCleaner cleaner = cleaner(executor)) {
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertNull(q.poll());
+                    threadsStarted.await();
+                    assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
+                    assertTrue(q.isEmpty());
+                }});
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadsStarted.await();
+                    q.put(one);
+                }});
+        }
+    }
+
+    /**
+     * a deserialized serialized queue is usable
+     */
+    public void testSerialization() {
+        final SynchronousQueue x = new SynchronousQueue();
+        final SynchronousQueue y = new SynchronousQueue(false);
+        final SynchronousQueue z = new SynchronousQueue(true);
+        assertSerialEquals(x, y);
+        assertNotSerialEquals(x, z);
+        SynchronousQueue[] qs = { x, y, z };
+        for (SynchronousQueue q : qs) {
+            SynchronousQueue clone = serialClone(q);
+            assertNotSame(q, clone);
+            assertSerialEquals(q, clone);
+            assertTrue(clone.isEmpty());
+            assertEquals(0, clone.size());
+            assertEquals(0, clone.remainingCapacity());
+            assertFalse(clone.offer(zero));
+        }
+    }
+
+    /**
+     * drainTo(c) of empty queue doesn't transfer elements
+     */
+    public void testDrainTo()      { testDrainTo(false); }
+    public void testDrainTo_fair() { testDrainTo(true); }
+    public void testDrainTo(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(0, l.size());
+    }
+
+    /**
+     * drainTo empties queue, unblocking a waiting put.
+     */
+    public void testDrainToWithActivePut()      { testDrainToWithActivePut(false); }
+    public void testDrainToWithActivePut_fair() { testDrainToWithActivePut(true); }
+    public void testDrainToWithActivePut(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(one);
+            }});
+
+        ArrayList l = new ArrayList();
+        long startTime = System.nanoTime();
+        while (l.isEmpty()) {
+            q.drainTo(l);
+            if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                fail("timed out");
+            Thread.yield();
+        }
+        assertTrue(l.size() == 1);
+        assertSame(one, l.get(0));
+        awaitTermination(t);
+    }
+
+    /**
+     * drainTo(c, n) empties up to n elements of queue into c
+     */
+    public void testDrainToN() throws InterruptedException {
+        final SynchronousQueue q = new SynchronousQueue();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(one);
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(two);
+            }});
+
+        ArrayList l = new ArrayList();
+        int drained;
+        while ((drained = q.drainTo(l, 1)) == 0) Thread.yield();
+        assertEquals(1, drained);
+        assertEquals(1, l.size());
+        while ((drained = q.drainTo(l, 1)) == 0) Thread.yield();
+        assertEquals(1, drained);
+        assertEquals(2, l.size());
+        assertTrue(l.contains(one));
+        assertTrue(l.contains(two));
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * remove(null), contains(null) always return false
+     */
+    public void testNeverContainsNull() {
+        Collection<?> q = new SynchronousQueue();
+        assertFalse(q.contains(null));
+        assertFalse(q.remove(null));
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/SystemTest.java b/jdk/test/java/util/concurrent/tck/SystemTest.java
new file mode 100644
index 00000000000..abf3b49cdd5
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/SystemTest.java
@@ -0,0 +1,96 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class SystemTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(SystemTest.class);
+    }
+
+    /**
+     * Worst case rounding for millisecs; set for 60 cycle millis clock.
+     * This value might need to be changed on JVMs with coarser
+     * System.currentTimeMillis clocks.
+     */
+    static final long MILLIS_ROUND = 17;
+
+    /**
+     * Nanos between readings of millis is no longer than millis (plus
+     * possible rounding).
+     * This shows only that nano timing not (much) worse than milli.
+     */
+    public void testNanoTime1() throws InterruptedException {
+        long m1 = System.currentTimeMillis();
+        Thread.sleep(1);
+        long n1 = System.nanoTime();
+        Thread.sleep(SHORT_DELAY_MS);
+        long n2 = System.nanoTime();
+        Thread.sleep(1);
+        long m2 = System.currentTimeMillis();
+        long millis = m2 - m1;
+        long nanos = n2 - n1;
+        assertTrue(nanos >= 0);
+        long nanosAsMillis = nanos / 1000000;
+        assertTrue(nanosAsMillis <= millis + MILLIS_ROUND);
+    }
+
+    /**
+     * Millis between readings of nanos is less than nanos, adjusting
+     * for rounding.
+     * This shows only that nano timing not (much) worse than milli.
+     */
+    public void testNanoTime2() throws InterruptedException {
+        long n1 = System.nanoTime();
+        Thread.sleep(1);
+        long m1 = System.currentTimeMillis();
+        Thread.sleep(SHORT_DELAY_MS);
+        long m2 = System.currentTimeMillis();
+        Thread.sleep(1);
+        long n2 = System.nanoTime();
+        long millis = m2 - m1;
+        long nanos = n2 - n1;
+
+        assertTrue(nanos >= 0);
+        long nanosAsMillis = nanos / 1000000;
+        assertTrue(millis <= nanosAsMillis + MILLIS_ROUND);
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/ThreadLocalRandom8Test.java b/jdk/test/java/util/concurrent/tck/ThreadLocalRandom8Test.java
new file mode 100644
index 00000000000..c80af6290ec
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/ThreadLocalRandom8Test.java
@@ -0,0 +1,262 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.LongAdder;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ThreadLocalRandom8Test extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ThreadLocalRandom8Test.class);
+    }
+
+    // max sampled int bound
+    static final int MAX_INT_BOUND = (1 << 26);
+
+    // max sampled long bound
+    static final long MAX_LONG_BOUND = (1L << 42);
+
+    // Number of replications for other checks
+    static final int REPS =
+        Integer.getInteger("ThreadLocalRandom8Test.reps", 4);
+
+    /**
+     * Invoking sized ints, long, doubles, with negative sizes throws
+     * IllegalArgumentException
+     */
+    public void testBadStreamSize() {
+        ThreadLocalRandom r = ThreadLocalRandom.current();
+        Runnable[] throwingActions = {
+            () -> r.ints(-1L),
+            () -> r.ints(-1L, 2, 3),
+            () -> r.longs(-1L),
+            () -> r.longs(-1L, -1L, 1L),
+            () -> r.doubles(-1L),
+            () -> r.doubles(-1L, .5, .6),
+        };
+        assertThrows(IllegalArgumentException.class, throwingActions);
+    }
+
+    /**
+     * Invoking bounded ints, long, doubles, with illegal bounds throws
+     * IllegalArgumentException
+     */
+    public void testBadStreamBounds() {
+        ThreadLocalRandom r = ThreadLocalRandom.current();
+        Runnable[] throwingActions = {
+            () -> r.ints(2, 1),
+            () -> r.ints(10, 42, 42),
+            () -> r.longs(-1L, -1L),
+            () -> r.longs(10, 1L, -2L),
+            () -> r.doubles(0.0, 0.0),
+            () -> r.doubles(10, .5, .4),
+        };
+        assertThrows(IllegalArgumentException.class, throwingActions);
+    }
+
+    /**
+     * A parallel sized stream of ints generates the given number of values
+     */
+    public void testIntsCount() {
+        LongAdder counter = new LongAdder();
+        ThreadLocalRandom r = ThreadLocalRandom.current();
+        long size = 0;
+        for (int reps = 0; reps < REPS; ++reps) {
+            counter.reset();
+            r.ints(size).parallel().forEach(x -> counter.increment());
+            assertEquals(size, counter.sum());
+            size += 524959;
+        }
+    }
+
+    /**
+     * A parallel sized stream of longs generates the given number of values
+     */
+    public void testLongsCount() {
+        LongAdder counter = new LongAdder();
+        ThreadLocalRandom r = ThreadLocalRandom.current();
+        long size = 0;
+        for (int reps = 0; reps < REPS; ++reps) {
+            counter.reset();
+            r.longs(size).parallel().forEach(x -> counter.increment());
+            assertEquals(size, counter.sum());
+            size += 524959;
+        }
+    }
+
+    /**
+     * A parallel sized stream of doubles generates the given number of values
+     */
+    public void testDoublesCount() {
+        LongAdder counter = new LongAdder();
+        ThreadLocalRandom r = ThreadLocalRandom.current();
+        long size = 0;
+        for (int reps = 0; reps < REPS; ++reps) {
+            counter.reset();
+            r.doubles(size).parallel().forEach(x -> counter.increment());
+            assertEquals(size, counter.sum());
+            size += 524959;
+        }
+    }
+
+    /**
+     * Each of a parallel sized stream of bounded ints is within bounds
+     */
+    public void testBoundedInts() {
+        AtomicInteger fails = new AtomicInteger(0);
+        ThreadLocalRandom r = ThreadLocalRandom.current();
+        long size = 12345L;
+        for (int least = -15485867; least < MAX_INT_BOUND; least += 524959) {
+            for (int bound = least + 2; bound > least && bound < MAX_INT_BOUND; bound += 67867967) {
+                final int lo = least, hi = bound;
+                r.ints(size, lo, hi).parallel().forEach(
+                    x -> {
+                        if (x < lo || x >= hi)
+                            fails.getAndIncrement(); });
+            }
+        }
+        assertEquals(0, fails.get());
+    }
+
+    /**
+     * Each of a parallel sized stream of bounded longs is within bounds
+     */
+    public void testBoundedLongs() {
+        AtomicInteger fails = new AtomicInteger(0);
+        ThreadLocalRandom r = ThreadLocalRandom.current();
+        long size = 123L;
+        for (long least = -86028121; least < MAX_LONG_BOUND; least += 1982451653L) {
+            for (long bound = least + 2; bound > least && bound < MAX_LONG_BOUND; bound += Math.abs(bound * 7919)) {
+                final long lo = least, hi = bound;
+                r.longs(size, lo, hi).parallel().forEach(
+                    x -> {
+                        if (x < lo || x >= hi)
+                            fails.getAndIncrement(); });
+            }
+        }
+        assertEquals(0, fails.get());
+    }
+
+    /**
+     * Each of a parallel sized stream of bounded doubles is within bounds
+     */
+    public void testBoundedDoubles() {
+        AtomicInteger fails = new AtomicInteger(0);
+        ThreadLocalRandom r = ThreadLocalRandom.current();
+        long size = 456;
+        for (double least = 0.00011; least < 1.0e20; least *= 9) {
+            for (double bound = least * 1.0011; bound < 1.0e20; bound *= 17) {
+                final double lo = least, hi = bound;
+                r.doubles(size, lo, hi).parallel().forEach(
+                    x -> {
+                        if (x < lo || x >= hi)
+                            fails.getAndIncrement(); });
+            }
+        }
+        assertEquals(0, fails.get());
+    }
+
+    /**
+     * A parallel unsized stream of ints generates at least 100 values
+     */
+    public void testUnsizedIntsCount() {
+        LongAdder counter = new LongAdder();
+        ThreadLocalRandom r = ThreadLocalRandom.current();
+        long size = 100;
+        r.ints().limit(size).parallel().forEach(x -> counter.increment());
+        assertEquals(size, counter.sum());
+    }
+
+    /**
+     * A parallel unsized stream of longs generates at least 100 values
+     */
+    public void testUnsizedLongsCount() {
+        LongAdder counter = new LongAdder();
+        ThreadLocalRandom r = ThreadLocalRandom.current();
+        long size = 100;
+        r.longs().limit(size).parallel().forEach(x -> counter.increment());
+        assertEquals(size, counter.sum());
+    }
+
+    /**
+     * A parallel unsized stream of doubles generates at least 100 values
+     */
+    public void testUnsizedDoublesCount() {
+        LongAdder counter = new LongAdder();
+        ThreadLocalRandom r = ThreadLocalRandom.current();
+        long size = 100;
+        r.doubles().limit(size).parallel().forEach(x -> counter.increment());
+        assertEquals(size, counter.sum());
+    }
+
+    /**
+     * A sequential unsized stream of ints generates at least 100 values
+     */
+    public void testUnsizedIntsCountSeq() {
+        LongAdder counter = new LongAdder();
+        ThreadLocalRandom r = ThreadLocalRandom.current();
+        long size = 100;
+        r.ints().limit(size).forEach(x -> counter.increment());
+        assertEquals(size, counter.sum());
+    }
+
+    /**
+     * A sequential unsized stream of longs generates at least 100 values
+     */
+    public void testUnsizedLongsCountSeq() {
+        LongAdder counter = new LongAdder();
+        ThreadLocalRandom r = ThreadLocalRandom.current();
+        long size = 100;
+        r.longs().limit(size).forEach(x -> counter.increment());
+        assertEquals(size, counter.sum());
+    }
+
+    /**
+     * A sequential unsized stream of doubles generates at least 100 values
+     */
+    public void testUnsizedDoublesCountSeq() {
+        LongAdder counter = new LongAdder();
+        ThreadLocalRandom r = ThreadLocalRandom.current();
+        long size = 100;
+        r.doubles().limit(size).forEach(x -> counter.increment());
+        assertEquals(size, counter.sum());
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/ThreadLocalRandomTest.java b/jdk/test/java/util/concurrent/tck/ThreadLocalRandomTest.java
new file mode 100644
index 00000000000..da50bbe2495
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/ThreadLocalRandomTest.java
@@ -0,0 +1,368 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ThreadLocalRandomTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ThreadLocalRandomTest.class);
+    }
+
+    /*
+     * Testing coverage notes:
+     *
+     * We don't test randomness properties, but only that repeated
+     * calls, up to NCALLS tries, produce at least one different
+     * result.  For bounded versions, we sample various intervals
+     * across multiples of primes.
+     */
+
+    // max numbers of calls to detect getting stuck on one value
+    static final int NCALLS = 10000;
+
+    // max sampled int bound
+    static final int MAX_INT_BOUND = (1 << 28);
+
+    // max sampled long bound
+    static final long MAX_LONG_BOUND = (1L << 42);
+
+    // Number of replications for other checks
+    static final int REPS = 20;
+
+    /**
+     * setSeed throws UnsupportedOperationException
+     */
+    public void testSetSeed() {
+        try {
+            ThreadLocalRandom.current().setSeed(17);
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+    }
+
+    /**
+     * Repeated calls to nextInt produce at least two distinct results
+     */
+    public void testNextInt() {
+        int f = ThreadLocalRandom.current().nextInt();
+        int i = 0;
+        while (i < NCALLS && ThreadLocalRandom.current().nextInt() == f)
+            ++i;
+        assertTrue(i < NCALLS);
+    }
+
+    /**
+     * Repeated calls to nextLong produce at least two distinct results
+     */
+    public void testNextLong() {
+        long f = ThreadLocalRandom.current().nextLong();
+        int i = 0;
+        while (i < NCALLS && ThreadLocalRandom.current().nextLong() == f)
+            ++i;
+        assertTrue(i < NCALLS);
+    }
+
+    /**
+     * Repeated calls to nextBoolean produce at least two distinct results
+     */
+    public void testNextBoolean() {
+        boolean f = ThreadLocalRandom.current().nextBoolean();
+        int i = 0;
+        while (i < NCALLS && ThreadLocalRandom.current().nextBoolean() == f)
+            ++i;
+        assertTrue(i < NCALLS);
+    }
+
+    /**
+     * Repeated calls to nextFloat produce at least two distinct results
+     */
+    public void testNextFloat() {
+        float f = ThreadLocalRandom.current().nextFloat();
+        int i = 0;
+        while (i < NCALLS && ThreadLocalRandom.current().nextFloat() == f)
+            ++i;
+        assertTrue(i < NCALLS);
+    }
+
+    /**
+     * Repeated calls to nextDouble produce at least two distinct results
+     */
+    public void testNextDouble() {
+        double f = ThreadLocalRandom.current().nextDouble();
+        int i = 0;
+        while (i < NCALLS && ThreadLocalRandom.current().nextDouble() == f)
+            ++i;
+        assertTrue(i < NCALLS);
+    }
+
+    /**
+     * Repeated calls to nextGaussian produce at least two distinct results
+     */
+    public void testNextGaussian() {
+        double f = ThreadLocalRandom.current().nextGaussian();
+        int i = 0;
+        while (i < NCALLS && ThreadLocalRandom.current().nextGaussian() == f)
+            ++i;
+        assertTrue(i < NCALLS);
+    }
+
+    /**
+     * nextInt(non-positive) throws IllegalArgumentException
+     */
+    public void testNextIntBoundNonPositive() {
+        ThreadLocalRandom rnd = ThreadLocalRandom.current();
+        for (int bound : new int[] { 0, -17, Integer.MIN_VALUE }) {
+            try {
+                rnd.nextInt(bound);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * nextInt(least >= bound) throws IllegalArgumentException
+     */
+    public void testNextIntBadBounds() {
+        int[][] badBoundss = {
+            { 17, 2 },
+            { -42, -42 },
+            { Integer.MAX_VALUE, Integer.MIN_VALUE },
+        };
+        ThreadLocalRandom rnd = ThreadLocalRandom.current();
+        for (int[] badBounds : badBoundss) {
+            try {
+                rnd.nextInt(badBounds[0], badBounds[1]);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * nextInt(bound) returns 0 <= value < bound;
+     * repeated calls produce at least two distinct results
+     */
+    public void testNextIntBounded() {
+        // sample bound space across prime number increments
+        for (int bound = 2; bound < MAX_INT_BOUND; bound += 524959) {
+            int f = ThreadLocalRandom.current().nextInt(bound);
+            assertTrue(0 <= f && f < bound);
+            int i = 0;
+            int j;
+            while (i < NCALLS &&
+                   (j = ThreadLocalRandom.current().nextInt(bound)) == f) {
+                assertTrue(0 <= j && j < bound);
+                ++i;
+            }
+            assertTrue(i < NCALLS);
+        }
+    }
+
+    /**
+     * nextInt(least, bound) returns least <= value < bound;
+     * repeated calls produce at least two distinct results
+     */
+    public void testNextIntBounded2() {
+        for (int least = -15485863; least < MAX_INT_BOUND; least += 524959) {
+            for (int bound = least + 2; bound > least && bound < MAX_INT_BOUND; bound += 49979687) {
+                int f = ThreadLocalRandom.current().nextInt(least, bound);
+                assertTrue(least <= f && f < bound);
+                int i = 0;
+                int j;
+                while (i < NCALLS &&
+                       (j = ThreadLocalRandom.current().nextInt(least, bound)) == f) {
+                    assertTrue(least <= j && j < bound);
+                    ++i;
+                }
+                assertTrue(i < NCALLS);
+            }
+        }
+    }
+
+    /**
+     * nextLong(non-positive) throws IllegalArgumentException
+     */
+    public void testNextLongBoundNonPositive() {
+        ThreadLocalRandom rnd = ThreadLocalRandom.current();
+        for (long bound : new long[] { 0L, -17L, Long.MIN_VALUE }) {
+            try {
+                rnd.nextLong(bound);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * nextLong(least >= bound) throws IllegalArgumentException
+     */
+    public void testNextLongBadBounds() {
+        long[][] badBoundss = {
+            { 17L, 2L },
+            { -42L, -42L },
+            { Long.MAX_VALUE, Long.MIN_VALUE },
+        };
+        ThreadLocalRandom rnd = ThreadLocalRandom.current();
+        for (long[] badBounds : badBoundss) {
+            try {
+                rnd.nextLong(badBounds[0], badBounds[1]);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * nextLong(bound) returns 0 <= value < bound;
+     * repeated calls produce at least two distinct results
+     */
+    public void testNextLongBounded() {
+        for (long bound = 2; bound < MAX_LONG_BOUND; bound += 15485863) {
+            long f = ThreadLocalRandom.current().nextLong(bound);
+            assertTrue(0 <= f && f < bound);
+            int i = 0;
+            long j;
+            while (i < NCALLS &&
+                   (j = ThreadLocalRandom.current().nextLong(bound)) == f) {
+                assertTrue(0 <= j && j < bound);
+                ++i;
+            }
+            assertTrue(i < NCALLS);
+        }
+    }
+
+    /**
+     * nextLong(least, bound) returns least <= value < bound;
+     * repeated calls produce at least two distinct results
+     */
+    public void testNextLongBounded2() {
+        for (long least = -86028121; least < MAX_LONG_BOUND; least += 982451653L) {
+            for (long bound = least + 2; bound > least && bound < MAX_LONG_BOUND; bound += Math.abs(bound * 7919)) {
+                long f = ThreadLocalRandom.current().nextLong(least, bound);
+                assertTrue(least <= f && f < bound);
+                int i = 0;
+                long j;
+                while (i < NCALLS &&
+                       (j = ThreadLocalRandom.current().nextLong(least, bound)) == f) {
+                    assertTrue(least <= j && j < bound);
+                    ++i;
+                }
+                assertTrue(i < NCALLS);
+            }
+        }
+    }
+
+    /**
+     * nextDouble(non-positive) throws IllegalArgumentException
+     */
+    public void testNextDoubleBoundNonPositive() {
+        ThreadLocalRandom rnd = ThreadLocalRandom.current();
+        double[] badBounds = {
+            0.0d,
+            -17.0d,
+            -Double.MIN_VALUE,
+            Double.NEGATIVE_INFINITY,
+            Double.NaN,
+        };
+        for (double bound : badBounds) {
+            try {
+                rnd.nextDouble(bound);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * nextDouble(least, bound) returns least <= value < bound;
+     * repeated calls produce at least two distinct results
+     */
+    public void testNextDoubleBounded2() {
+        for (double least = 0.0001; least < 1.0e20; least *= 8) {
+            for (double bound = least * 1.001; bound < 1.0e20; bound *= 16) {
+                double f = ThreadLocalRandom.current().nextDouble(least, bound);
+                assertTrue(least <= f && f < bound);
+                int i = 0;
+                double j;
+                while (i < NCALLS &&
+                       (j = ThreadLocalRandom.current().nextDouble(least, bound)) == f) {
+                    assertTrue(least <= j && j < bound);
+                    ++i;
+                }
+                assertTrue(i < NCALLS);
+            }
+        }
+    }
+
+    /**
+     * Different threads produce different pseudo-random sequences
+     */
+    public void testDifferentSequences() {
+        // Don't use main thread's ThreadLocalRandom - it is likely to
+        // be polluted by previous tests.
+        final AtomicReference<ThreadLocalRandom> threadLocalRandom =
+            new AtomicReference<ThreadLocalRandom>();
+        final AtomicLong rand = new AtomicLong();
+
+        long firstRand = 0;
+        ThreadLocalRandom firstThreadLocalRandom = null;
+
+        Runnable getRandomState = new CheckedRunnable() {
+            public void realRun() {
+                ThreadLocalRandom current = ThreadLocalRandom.current();
+                assertSame(current, ThreadLocalRandom.current());
+                // test bug: the following is not guaranteed and not true in JDK8
+                //                assertNotSame(current, threadLocalRandom.get());
+                rand.set(current.nextLong());
+                threadLocalRandom.set(current);
+            }};
+
+        Thread first = newStartedThread(getRandomState);
+        awaitTermination(first);
+        firstRand = rand.get();
+        firstThreadLocalRandom = threadLocalRandom.get();
+
+        for (int i = 0; i < NCALLS; i++) {
+            Thread t = newStartedThread(getRandomState);
+            awaitTermination(t);
+            if (firstRand != rand.get())
+                return;
+        }
+        fail("all threads generate the same pseudo-random sequence");
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/ThreadLocalTest.java b/jdk/test/java/util/concurrent/tck/ThreadLocalTest.java
new file mode 100644
index 00000000000..51d2197f4d7
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/ThreadLocalTest.java
@@ -0,0 +1,128 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ThreadLocalTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(ThreadLocalTest.class);
+    }
+
+    static ThreadLocal<Integer> tl = new ThreadLocal<Integer>() {
+            public Integer initialValue() {
+                return one;
+            }
+        };
+
+    static InheritableThreadLocal<Integer> itl =
+        new InheritableThreadLocal<Integer>() {
+            protected Integer initialValue() {
+                return zero;
+            }
+
+            protected Integer childValue(Integer parentValue) {
+                return new Integer(parentValue.intValue() + 1);
+            }
+        };
+
+    /**
+     * remove causes next access to return initial value
+     */
+    public void testRemove() {
+        assertSame(tl.get(), one);
+        tl.set(two);
+        assertSame(tl.get(), two);
+        tl.remove();
+        assertSame(tl.get(), one);
+    }
+
+    /**
+     * remove in InheritableThreadLocal causes next access to return
+     * initial value
+     */
+    public void testRemoveITL() {
+        assertSame(itl.get(), zero);
+        itl.set(two);
+        assertSame(itl.get(), two);
+        itl.remove();
+        assertSame(itl.get(), zero);
+    }
+
+    private class ITLThread extends Thread {
+        final int[] x;
+        ITLThread(int[] array) { x = array; }
+        public void run() {
+            Thread child = null;
+            if (itl.get().intValue() < x.length - 1) {
+                child = new ITLThread(x);
+                child.start();
+            }
+            Thread.yield();
+
+            int threadId = itl.get().intValue();
+            for (int j = 0; j < threadId; j++) {
+                x[threadId]++;
+                Thread.yield();
+            }
+
+            if (child != null) { // Wait for child (if any)
+                try {
+                    child.join();
+                } catch (InterruptedException e) {
+                    threadUnexpectedException(e);
+                }
+            }
+        }
+    }
+
+    /**
+     * InheritableThreadLocal propagates generic values.
+     */
+    public void testGenericITL() throws InterruptedException {
+        final int threadCount = 10;
+        final int[] x = new int[threadCount];
+        Thread progenitor = new ITLThread(x);
+        progenitor.start();
+        progenitor.join();
+        for (int i = 0; i < threadCount; i++) {
+            assertEquals(i, x[i]);
+        }
+    }
+}
diff --git a/jdk/test/java/util/concurrent/tck/ThreadPoolExecutorSubclassTest.java b/jdk/test/java/util/concurrent/tck/ThreadPoolExecutorSubclassTest.java
new file mode 100644
index 00000000000..3959527af70
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/ThreadPoolExecutorSubclassTest.java
@@ -0,0 +1,2062 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.RunnableFuture;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ThreadPoolExecutorSubclassTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ThreadPoolExecutorSubclassTest.class);
+    }
+
+    static class CustomTask<V> implements RunnableFuture<V> {
+        final Callable<V> callable;
+        final ReentrantLock lock = new ReentrantLock();
+        final Condition cond = lock.newCondition();
+        boolean done;
+        boolean cancelled;
+        V result;
+        Thread thread;
+        Exception exception;
+        CustomTask(Callable<V> c) {
+            if (c == null) throw new NullPointerException();
+            callable = c;
+        }
+        CustomTask(final Runnable r, final V res) {
+            if (r == null) throw new NullPointerException();
+            callable = new Callable<V>() {
+                public V call() throws Exception { r.run(); return res; }};
+        }
+        public boolean isDone() {
+            lock.lock(); try { return done; } finally { lock.unlock() ; }
+        }
+        public boolean isCancelled() {
+            lock.lock(); try { return cancelled; } finally { lock.unlock() ; }
+        }
+        public boolean cancel(boolean mayInterrupt) {
+            lock.lock();
+            try {
+                if (!done) {
+                    cancelled = true;
+                    done = true;
+                    if (mayInterrupt && thread != null)
+                        thread.interrupt();
+                    return true;
+                }
+                return false;
+            }
+            finally { lock.unlock() ; }
+        }
+        public void run() {
+            lock.lock();
+            try {
+                if (done)
+                    return;
+                thread = Thread.currentThread();
+            }
+            finally { lock.unlock() ; }
+            V v = null;
+            Exception e = null;
+            try {
+                v = callable.call();
+            }
+            catch (Exception ex) {
+                e = ex;
+            }
+            lock.lock();
+            try {
+                if (!done) {
+                    result = v;
+                    exception = e;
+                    done = true;
+                    thread = null;
+                    cond.signalAll();
+                }
+            }
+            finally { lock.unlock(); }
+        }
+        public V get() throws InterruptedException, ExecutionException {
+            lock.lock();
+            try {
+                while (!done)
+                    cond.await();
+                if (cancelled)
+                    throw new CancellationException();
+                if (exception != null)
+                    throw new ExecutionException(exception);
+                return result;
+            }
+            finally { lock.unlock(); }
+        }
+        public V get(long timeout, TimeUnit unit)
+            throws InterruptedException, ExecutionException, TimeoutException {
+            long nanos = unit.toNanos(timeout);
+            lock.lock();
+            try {
+                while (!done) {
+                    if (nanos <= 0L)
+                        throw new TimeoutException();
+                    nanos = cond.awaitNanos(nanos);
+                }
+                if (cancelled)
+                    throw new CancellationException();
+                if (exception != null)
+                    throw new ExecutionException(exception);
+                return result;
+            }
+            finally { lock.unlock(); }
+        }
+    }
+
+    static class CustomTPE extends ThreadPoolExecutor {
+        protected <V> RunnableFuture<V> newTaskFor(Callable<V> c) {
+            return new CustomTask<V>(c);
+        }
+        protected <V> RunnableFuture<V> newTaskFor(Runnable r, V v) {
+            return new CustomTask<V>(r, v);
+        }
+
+        CustomTPE(int corePoolSize,
+                  int maximumPoolSize,
+                  long keepAliveTime,
+                  TimeUnit unit,
+                  BlockingQueue<Runnable> workQueue) {
+            super(corePoolSize, maximumPoolSize, keepAliveTime, unit,
+                  workQueue);
+        }
+        CustomTPE(int corePoolSize,
+                  int maximumPoolSize,
+                  long keepAliveTime,
+                  TimeUnit unit,
+                  BlockingQueue<Runnable> workQueue,
+                  ThreadFactory threadFactory) {
+        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
+             threadFactory);
+        }
+
+        CustomTPE(int corePoolSize,
+                  int maximumPoolSize,
+                  long keepAliveTime,
+                  TimeUnit unit,
+                  BlockingQueue<Runnable> workQueue,
+                  RejectedExecutionHandler handler) {
+        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
+              handler);
+        }
+        CustomTPE(int corePoolSize,
+                  int maximumPoolSize,
+                  long keepAliveTime,
+                  TimeUnit unit,
+                  BlockingQueue<Runnable> workQueue,
+                  ThreadFactory threadFactory,
+                  RejectedExecutionHandler handler) {
+            super(corePoolSize, maximumPoolSize, keepAliveTime, unit,
+              workQueue, threadFactory, handler);
+        }
+
+        final CountDownLatch beforeCalled = new CountDownLatch(1);
+        final CountDownLatch afterCalled = new CountDownLatch(1);
+        final CountDownLatch terminatedCalled = new CountDownLatch(1);
+
+        public CustomTPE() {
+            super(1, 1, LONG_DELAY_MS, MILLISECONDS, new SynchronousQueue<Runnable>());
+        }
+        protected void beforeExecute(Thread t, Runnable r) {
+            beforeCalled.countDown();
+        }
+        protected void afterExecute(Runnable r, Throwable t) {
+            afterCalled.countDown();
+        }
+        protected void terminated() {
+            terminatedCalled.countDown();
+        }
+
+        public boolean beforeCalled() {
+            return beforeCalled.getCount() == 0;
+        }
+        public boolean afterCalled() {
+            return afterCalled.getCount() == 0;
+        }
+        public boolean terminatedCalled() {
+            return terminatedCalled.getCount() == 0;
+        }
+    }
+
+    static class FailingThreadFactory implements ThreadFactory {
+        int calls = 0;
+        public Thread newThread(Runnable r) {
+            if (++calls > 1) return null;
+            return new Thread(r);
+        }
+    }
+
+    /**
+     * execute successfully executes a runnable
+     */
+    public void testExecute() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          2 * LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch done = new CountDownLatch(1);
+            final Runnable task = new CheckedRunnable() {
+                public void realRun() { done.countDown(); }};
+            p.execute(task);
+            assertTrue(done.await(LONG_DELAY_MS, MILLISECONDS));
+        }
+    }
+
+    /**
+     * getActiveCount increases but doesn't overestimate, when a
+     * thread becomes active
+     */
+    public void testGetActiveCount() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            assertEquals(0, p.getActiveCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getActiveCount());
+                    await(done);
+                }});
+            await(threadStarted);
+            assertEquals(1, p.getActiveCount());
+        }
+    }
+
+    /**
+     * prestartCoreThread starts a thread if under corePoolSize, else doesn't
+     */
+    public void testPrestartCoreThread() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(2, 6,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertEquals(0, p.getPoolSize());
+            assertTrue(p.prestartCoreThread());
+            assertEquals(1, p.getPoolSize());
+            assertTrue(p.prestartCoreThread());
+            assertEquals(2, p.getPoolSize());
+            assertFalse(p.prestartCoreThread());
+            assertEquals(2, p.getPoolSize());
+            p.setCorePoolSize(4);
+            assertTrue(p.prestartCoreThread());
+            assertEquals(3, p.getPoolSize());
+            assertTrue(p.prestartCoreThread());
+            assertEquals(4, p.getPoolSize());
+            assertFalse(p.prestartCoreThread());
+            assertEquals(4, p.getPoolSize());
+        }
+    }
+
+    /**
+     * prestartAllCoreThreads starts all corePoolSize threads
+     */
+    public void testPrestartAllCoreThreads() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(2, 6,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertEquals(0, p.getPoolSize());
+            p.prestartAllCoreThreads();
+            assertEquals(2, p.getPoolSize());
+            p.prestartAllCoreThreads();
+            assertEquals(2, p.getPoolSize());
+            p.setCorePoolSize(4);
+            p.prestartAllCoreThreads();
+            assertEquals(4, p.getPoolSize());
+            p.prestartAllCoreThreads();
+            assertEquals(4, p.getPoolSize());
+        }
+    }
+
+    /**
+     * getCompletedTaskCount increases, but doesn't overestimate,
+     * when tasks complete
+     */
+    public void testGetCompletedTaskCount() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            final CountDownLatch threadProceed = new CountDownLatch(1);
+            final CountDownLatch threadDone = new CountDownLatch(1);
+            assertEquals(0, p.getCompletedTaskCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(0, p.getCompletedTaskCount());
+                    threadProceed.await();
+                    threadDone.countDown();
+                }});
+            await(threadStarted);
+            assertEquals(0, p.getCompletedTaskCount());
+            threadProceed.countDown();
+            threadDone.await();
+            long startTime = System.nanoTime();
+            while (p.getCompletedTaskCount() != 1) {
+                if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                    fail("timed out");
+                Thread.yield();
+            }
+        }
+    }
+
+    /**
+     * getCorePoolSize returns size given in constructor if not otherwise set
+     */
+    public void testGetCorePoolSize() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertEquals(1, p.getCorePoolSize());
+        }
+    }
+
+    /**
+     * getKeepAliveTime returns value given in constructor if not otherwise set
+     */
+    public void testGetKeepAliveTime() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(2, 2,
+                          1000, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertEquals(1, p.getKeepAliveTime(SECONDS));
+        }
+    }
+
+    /**
+     * getThreadFactory returns factory in constructor if not set
+     */
+    public void testGetThreadFactory() {
+        final ThreadFactory threadFactory = new SimpleThreadFactory();
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          threadFactory,
+                          new NoOpREHandler());
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertSame(threadFactory, p.getThreadFactory());
+        }
+    }
+
+    /**
+     * setThreadFactory sets the thread factory returned by getThreadFactory
+     */
+    public void testSetThreadFactory() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            ThreadFactory threadFactory = new SimpleThreadFactory();
+            p.setThreadFactory(threadFactory);
+            assertSame(threadFactory, p.getThreadFactory());
+        }
+    }
+
+    /**
+     * setThreadFactory(null) throws NPE
+     */
+    public void testSetThreadFactoryNull() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.setThreadFactory(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * getRejectedExecutionHandler returns handler in constructor if not set
+     */
+    public void testGetRejectedExecutionHandler() {
+        final RejectedExecutionHandler handler = new NoOpREHandler();
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          handler);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertSame(handler, p.getRejectedExecutionHandler());
+        }
+    }
+
+    /**
+     * setRejectedExecutionHandler sets the handler returned by
+     * getRejectedExecutionHandler
+     */
+    public void testSetRejectedExecutionHandler() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            RejectedExecutionHandler handler = new NoOpREHandler();
+            p.setRejectedExecutionHandler(handler);
+            assertSame(handler, p.getRejectedExecutionHandler());
+        }
+    }
+
+    /**
+     * setRejectedExecutionHandler(null) throws NPE
+     */
+    public void testSetRejectedExecutionHandlerNull() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.setRejectedExecutionHandler(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * getLargestPoolSize increases, but doesn't overestimate, when
+     * multiple threads active
+     */
+    public void testGetLargestPoolSize() throws InterruptedException {
+        final int THREADS = 3;
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new CustomTPE(THREADS, THREADS,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            assertEquals(0, p.getLargestPoolSize());
+            final CountDownLatch threadsStarted = new CountDownLatch(THREADS);
+            for (int i = 0; i < THREADS; i++)
+                p.execute(new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadsStarted.countDown();
+                        await(done);
+                        assertEquals(THREADS, p.getLargestPoolSize());
+                    }});
+            await(threadsStarted);
+            assertEquals(THREADS, p.getLargestPoolSize());
+        }
+        assertEquals(THREADS, p.getLargestPoolSize());
+    }
+
+    /**
+     * getMaximumPoolSize returns value given in constructor if not
+     * otherwise set
+     */
+    public void testGetMaximumPoolSize() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(2, 3,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertEquals(3, p.getMaximumPoolSize());
+            p.setMaximumPoolSize(5);
+            assertEquals(5, p.getMaximumPoolSize());
+            p.setMaximumPoolSize(4);
+            assertEquals(4, p.getMaximumPoolSize());
+        }
+    }
+
+    /**
+     * getPoolSize increases, but doesn't overestimate, when threads
+     * become active
+     */
+    public void testGetPoolSize() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            assertEquals(0, p.getPoolSize());
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getPoolSize());
+                    await(done);
+                }});
+            await(threadStarted);
+            assertEquals(1, p.getPoolSize());
+        }
+    }
+
+    /**
+     * getTaskCount increases, but doesn't overestimate, when tasks submitted
+     */
+    public void testGetTaskCount() throws InterruptedException {
+        final int TASKS = 3;
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            assertEquals(0, p.getTaskCount());
+            assertEquals(0, p.getCompletedTaskCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    await(done);
+                }});
+            await(threadStarted);
+            assertEquals(1, p.getTaskCount());
+            assertEquals(0, p.getCompletedTaskCount());
+            for (int i = 0; i < TASKS; i++) {
+                assertEquals(1 + i, p.getTaskCount());
+                p.execute(new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadStarted.countDown();
+                        assertEquals(1 + TASKS, p.getTaskCount());
+                        await(done);
+                    }});
+            }
+            assertEquals(1 + TASKS, p.getTaskCount());
+            assertEquals(0, p.getCompletedTaskCount());
+        }
+        assertEquals(1 + TASKS, p.getTaskCount());
+        assertEquals(1 + TASKS, p.getCompletedTaskCount());
+    }
+
+    /**
+     * isShutdown is false before shutdown, true after
+     */
+    public void testIsShutdown() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertFalse(p.isShutdown());
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertTrue(p.isShutdown());
+        }
+    }
+
+    /**
+     * isTerminated is false before termination, true after
+     */
+    public void testIsTerminated() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            final CountDownLatch done = new CountDownLatch(1);
+            assertFalse(p.isTerminating());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(p.isTerminating());
+                    threadStarted.countDown();
+                    await(done);
+                }});
+            await(threadStarted);
+            assertFalse(p.isTerminating());
+            done.countDown();
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(p.isTerminated());
+            assertFalse(p.isTerminating());
+        }
+    }
+
+    /**
+     * isTerminating is not true when running or when terminated
+     */
+    public void testIsTerminating() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            final CountDownLatch done = new CountDownLatch(1);
+            assertFalse(p.isTerminating());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(p.isTerminating());
+                    threadStarted.countDown();
+                    await(done);
+                }});
+            await(threadStarted);
+            assertFalse(p.isTerminating());
+            done.countDown();
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(p.isTerminated());
+            assertFalse(p.isTerminating());
+        }
+    }
+
+    /**
+     * getQueue returns the work queue, which contains queued tasks
+     */
+    public void testGetQueue() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final BlockingQueue<Runnable> q = new ArrayBlockingQueue<Runnable>(10);
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          q);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            FutureTask[] tasks = new FutureTask[5];
+            for (int i = 0; i < tasks.length; i++) {
+                Callable task = new CheckedCallable<Boolean>() {
+                    public Boolean realCall() throws InterruptedException {
+                        threadStarted.countDown();
+                        assertSame(q, p.getQueue());
+                        await(done);
+                        return Boolean.TRUE;
+                    }};
+                tasks[i] = new FutureTask(task);
+                p.execute(tasks[i]);
+            }
+            await(threadStarted);
+            assertSame(q, p.getQueue());
+            assertFalse(q.contains(tasks[0]));
+            assertTrue(q.contains(tasks[tasks.length - 1]));
+            assertEquals(tasks.length - 1, q.size());
+        }
+    }
+
+    /**
+     * remove(task) removes queued task, and fails to remove active task
+     */
+    public void testRemove() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        BlockingQueue<Runnable> q = new ArrayBlockingQueue<Runnable>(10);
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          q);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            Runnable[] tasks = new Runnable[6];
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            for (int i = 0; i < tasks.length; i++) {
+                tasks[i] = new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadStarted.countDown();
+                        await(done);
+                    }};
+                p.execute(tasks[i]);
+            }
+            await(threadStarted);
+            assertFalse(p.remove(tasks[0]));
+            assertTrue(q.contains(tasks[4]));
+            assertTrue(q.contains(tasks[3]));
+            assertTrue(p.remove(tasks[4]));
+            assertFalse(p.remove(tasks[4]));
+            assertFalse(q.contains(tasks[4]));
+            assertTrue(q.contains(tasks[3]));
+            assertTrue(p.remove(tasks[3]));
+            assertFalse(q.contains(tasks[3]));
+        }
+    }
+
+    /**
+     * purge removes cancelled tasks from the queue
+     */
+    public void testPurge() throws InterruptedException {
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        final BlockingQueue<Runnable> q = new ArrayBlockingQueue<Runnable>(10);
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          q);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            FutureTask[] tasks = new FutureTask[5];
+            for (int i = 0; i < tasks.length; i++) {
+                Callable task = new CheckedCallable<Boolean>() {
+                    public Boolean realCall() throws InterruptedException {
+                        threadStarted.countDown();
+                        await(done);
+                        return Boolean.TRUE;
+                    }};
+                tasks[i] = new FutureTask(task);
+                p.execute(tasks[i]);
+            }
+            await(threadStarted);
+            assertEquals(tasks.length, p.getTaskCount());
+            assertEquals(tasks.length - 1, q.size());
+            assertEquals(1L, p.getActiveCount());
+            assertEquals(0L, p.getCompletedTaskCount());
+            tasks[4].cancel(true);
+            tasks[3].cancel(false);
+            p.purge();
+            assertEquals(tasks.length - 3, q.size());
+            assertEquals(tasks.length - 2, p.getTaskCount());
+            p.purge();         // Nothing to do
+            assertEquals(tasks.length - 3, q.size());
+            assertEquals(tasks.length - 2, p.getTaskCount());
+        }
+    }
+
+    /**
+     * shutdownNow returns a list containing tasks that were not run,
+     * and those tasks are drained from the queue
+     */
+    public void testShutdownNow() throws InterruptedException {
+        final int poolSize = 2;
+        final int count = 5;
+        final AtomicInteger ran = new AtomicInteger(0);
+        final ThreadPoolExecutor p =
+            new CustomTPE(poolSize, poolSize,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        final CountDownLatch threadsStarted = new CountDownLatch(poolSize);
+        Runnable waiter = new CheckedRunnable() { public void realRun() {
+            threadsStarted.countDown();
+            try {
+                MILLISECONDS.sleep(2 * LONG_DELAY_MS);
+            } catch (InterruptedException success) {}
+            ran.getAndIncrement();
+        }};
+        for (int i = 0; i < count; i++)
+            p.execute(waiter);
+        await(threadsStarted);
+        assertEquals(poolSize, p.getActiveCount());
+        assertEquals(0, p.getCompletedTaskCount());
+        final List<Runnable> queuedTasks;
+        try {
+            queuedTasks = p.shutdownNow();
+        } catch (SecurityException ok) {
+            return; // Allowed in case test doesn't have privs
+        }
+        assertTrue(p.isShutdown());
+        assertTrue(p.getQueue().isEmpty());
+        assertEquals(count - poolSize, queuedTasks.size());
+        assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+        assertEquals(poolSize, ran.get());
+        assertEquals(poolSize, p.getCompletedTaskCount());
+    }
+
+    // Exception Tests
+
+    /**
+     * Constructor throws if corePoolSize argument is less than zero
+     */
+    public void testConstructor1() {
+        try {
+            new CustomTPE(-1, 1, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is less than zero
+     */
+    public void testConstructor2() {
+        try {
+            new CustomTPE(1, -1, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is equal to zero
+     */
+    public void testConstructor3() {
+        try {
+            new CustomTPE(1, 0, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if keepAliveTime is less than zero
+     */
+    public void testConstructor4() {
+        try {
+            new CustomTPE(1, 2, -1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize is greater than the maximumPoolSize
+     */
+    public void testConstructor5() {
+        try {
+            new CustomTPE(2, 1, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if workQueue is set to null
+     */
+    public void testConstructorNullPointerException() {
+        try {
+            new CustomTPE(1, 2, 1L, SECONDS, null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize argument is less than zero
+     */
+    public void testConstructor6() {
+        try {
+            new CustomTPE(-1, 1, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is less than zero
+     */
+    public void testConstructor7() {
+        try {
+            new CustomTPE(1,-1, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is equal to zero
+     */
+    public void testConstructor8() {
+        try {
+            new CustomTPE(1, 0, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if keepAliveTime is less than zero
+     */
+    public void testConstructor9() {
+        try {
+            new CustomTPE(1, 2, -1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize is greater than the maximumPoolSize
+     */
+    public void testConstructor10() {
+        try {
+            new CustomTPE(2, 1, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if workQueue is set to null
+     */
+    public void testConstructorNullPointerException2() {
+        try {
+            new CustomTPE(1, 2, 1L, SECONDS, null, new SimpleThreadFactory());
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if threadFactory is set to null
+     */
+    public void testConstructorNullPointerException3() {
+        try {
+            new CustomTPE(1, 2, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          (ThreadFactory) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize argument is less than zero
+     */
+    public void testConstructor11() {
+        try {
+            new CustomTPE(-1, 1, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is less than zero
+     */
+    public void testConstructor12() {
+        try {
+            new CustomTPE(1, -1, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is equal to zero
+     */
+    public void testConstructor13() {
+        try {
+            new CustomTPE(1, 0, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if keepAliveTime is less than zero
+     */
+    public void testConstructor14() {
+        try {
+            new CustomTPE(1, 2, -1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize is greater than the maximumPoolSize
+     */
+    public void testConstructor15() {
+        try {
+            new CustomTPE(2, 1, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if workQueue is set to null
+     */
+    public void testConstructorNullPointerException4() {
+        try {
+            new CustomTPE(1, 2, 1L, SECONDS,
+                          null,
+                          new NoOpREHandler());
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if handler is set to null
+     */
+    public void testConstructorNullPointerException5() {
+        try {
+            new CustomTPE(1, 2, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          (RejectedExecutionHandler) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize argument is less than zero
+     */
+    public void testConstructor16() {
+        try {
+            new CustomTPE(-1, 1, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new SimpleThreadFactory(),
+                          new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is less than zero
+     */
+    public void testConstructor17() {
+        try {
+            new CustomTPE(1, -1, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new SimpleThreadFactory(),
+                          new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is equal to zero
+     */
+    public void testConstructor18() {
+        try {
+            new CustomTPE(1, 0, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new SimpleThreadFactory(),
+                          new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if keepAliveTime is less than zero
+     */
+    public void testConstructor19() {
+        try {
+            new CustomTPE(1, 2, -1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new SimpleThreadFactory(),
+                          new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize is greater than the maximumPoolSize
+     */
+    public void testConstructor20() {
+        try {
+            new CustomTPE(2, 1, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new SimpleThreadFactory(),
+                          new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if workQueue is null
+     */
+    public void testConstructorNullPointerException6() {
+        try {
+            new CustomTPE(1, 2, 1L, SECONDS,
+                          null,
+                          new SimpleThreadFactory(),
+                          new NoOpREHandler());
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if handler is null
+     */
+    public void testConstructorNullPointerException7() {
+        try {
+            new CustomTPE(1, 2, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new SimpleThreadFactory(),
+                          (RejectedExecutionHandler) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if ThreadFactory is null
+     */
+    public void testConstructorNullPointerException8() {
+        try {
+            new CustomTPE(1, 2, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          (ThreadFactory) null,
+                          new NoOpREHandler());
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * execute throws RejectedExecutionException if saturated.
+     */
+    public void testSaturatedExecute() {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(1));
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            Runnable task = new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    await(done);
+                }};
+            for (int i = 0; i < 2; ++i)
+                p.execute(task);
+            for (int i = 0; i < 2; ++i) {
+                try {
+                    p.execute(task);
+                    shouldThrow();
+                } catch (RejectedExecutionException success) {}
+                assertTrue(p.getTaskCount() <= 2);
+            }
+        }
+    }
+
+    /**
+     * executor using CallerRunsPolicy runs task if saturated.
+     */
+    public void testSaturatedExecute2() {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(1),
+                          new CustomTPE.CallerRunsPolicy());
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            Runnable blocker = new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    await(done);
+                }};
+            p.execute(blocker);
+            TrackedNoOpRunnable[] tasks = new TrackedNoOpRunnable[5];
+            for (int i = 0; i < tasks.length; i++)
+                tasks[i] = new TrackedNoOpRunnable();
+            for (int i = 0; i < tasks.length; i++)
+                p.execute(tasks[i]);
+            for (int i = 1; i < tasks.length; i++)
+                assertTrue(tasks[i].done);
+            assertFalse(tasks[0].done); // waiting in queue
+        }
+    }
+
+    /**
+     * executor using DiscardPolicy drops task if saturated.
+     */
+    public void testSaturatedExecute3() {
+        final TrackedNoOpRunnable[] tasks = new TrackedNoOpRunnable[5];
+        for (int i = 0; i < tasks.length; ++i)
+            tasks[i] = new TrackedNoOpRunnable();
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(1),
+                          new CustomTPE.DiscardPolicy());
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            p.execute(awaiter(done));
+
+            for (TrackedNoOpRunnable task : tasks)
+                p.execute(task);
+            for (int i = 1; i < tasks.length; i++)
+                assertFalse(tasks[i].done);
+        }
+        for (int i = 1; i < tasks.length; i++)
+            assertFalse(tasks[i].done);
+        assertTrue(tasks[0].done); // was waiting in queue
+    }
+
+    /**
+     * executor using DiscardOldestPolicy drops oldest task if saturated.
+     */
+    public void testSaturatedExecute4() {
+        final CountDownLatch done = new CountDownLatch(1);
+        LatchAwaiter r1 = awaiter(done);
+        LatchAwaiter r2 = awaiter(done);
+        LatchAwaiter r3 = awaiter(done);
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(1),
+                          new CustomTPE.DiscardOldestPolicy());
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            assertEquals(LatchAwaiter.NEW, r1.state);
+            assertEquals(LatchAwaiter.NEW, r2.state);
+            assertEquals(LatchAwaiter.NEW, r3.state);
+            p.execute(r1);
+            p.execute(r2);
+            assertTrue(p.getQueue().contains(r2));
+            p.execute(r3);
+            assertFalse(p.getQueue().contains(r2));
+            assertTrue(p.getQueue().contains(r3));
+        }
+        assertEquals(LatchAwaiter.DONE, r1.state);
+        assertEquals(LatchAwaiter.NEW, r2.state);
+        assertEquals(LatchAwaiter.DONE, r3.state);
+    }
+
+    /**
+     * execute throws RejectedExecutionException if shutdown
+     */
+    public void testRejectedExecutionExceptionOnShutdown() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(1));
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.execute(new NoOpRunnable());
+                shouldThrow();
+            } catch (RejectedExecutionException success) {}
+        }
+    }
+
+    /**
+     * execute using CallerRunsPolicy drops task on shutdown
+     */
+    public void testCallerRunsOnShutdown() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(1),
+                          new CustomTPE.CallerRunsPolicy());
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        try (PoolCleaner cleaner = cleaner(p)) {
+            TrackedNoOpRunnable r = new TrackedNoOpRunnable();
+            p.execute(r);
+            assertFalse(r.done);
+        }
+    }
+
+    /**
+     * execute using DiscardPolicy drops task on shutdown
+     */
+    public void testDiscardOnShutdown() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(1),
+                          new CustomTPE.DiscardPolicy());
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        try (PoolCleaner cleaner = cleaner(p)) {
+            TrackedNoOpRunnable r = new TrackedNoOpRunnable();
+            p.execute(r);
+            assertFalse(r.done);
+        }
+    }
+
+    /**
+     * execute using DiscardOldestPolicy drops task on shutdown
+     */
+    public void testDiscardOldestOnShutdown() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(1),
+                          new CustomTPE.DiscardOldestPolicy());
+
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        try (PoolCleaner cleaner = cleaner(p)) {
+            TrackedNoOpRunnable r = new TrackedNoOpRunnable();
+            p.execute(r);
+            assertFalse(r.done);
+        }
+    }
+
+    /**
+     * execute(null) throws NPE
+     */
+    public void testExecuteNull() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 2,
+                          1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.execute(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * setCorePoolSize of negative value throws IllegalArgumentException
+     */
+    public void testCorePoolSizeIllegalArgumentException() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.setCorePoolSize(-1);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * setMaximumPoolSize(int) throws IllegalArgumentException
+     * if given a value less the core pool size
+     */
+    public void testMaximumPoolSizeIllegalArgumentException() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(2, 3,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.setMaximumPoolSize(1);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * setMaximumPoolSize throws IllegalArgumentException
+     * if given a negative value
+     */
+    public void testMaximumPoolSizeIllegalArgumentException2() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(2, 3,
+                          LONG_DELAY_MS,
+                          MILLISECONDS,new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.setMaximumPoolSize(-1);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * setKeepAliveTime throws IllegalArgumentException
+     * when given a negative value
+     */
+    public void testKeepAliveTimeIllegalArgumentException() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(2, 3,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.setKeepAliveTime(-1, MILLISECONDS);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * terminated() is called on termination
+     */
+    public void testTerminated() {
+        CustomTPE p = new CustomTPE();
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertTrue(p.terminatedCalled());
+            assertTrue(p.isShutdown());
+        }
+    }
+
+    /**
+     * beforeExecute and afterExecute are called when executing task
+     */
+    public void testBeforeAfter() throws InterruptedException {
+        CustomTPE p = new CustomTPE();
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch done = new CountDownLatch(1);
+            p.execute(new CheckedRunnable() {
+                public void realRun() {
+                    done.countDown();
+                }});
+            await(p.afterCalled);
+            assertEquals(0, done.getCount());
+            assertTrue(p.afterCalled());
+            assertTrue(p.beforeCalled());
+        }
+    }
+
+    /**
+     * completed submit of callable returns result
+     */
+    public void testSubmitCallable() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<String> future = e.submit(new StringTask());
+            String result = future.get();
+            assertSame(TEST_STRING, result);
+        }
+    }
+
+    /**
+     * completed submit of runnable returns successfully
+     */
+    public void testSubmitRunnable() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<?> future = e.submit(new NoOpRunnable());
+            future.get();
+            assertTrue(future.isDone());
+        }
+    }
+
+    /**
+     * completed submit of (runnable, result) returns result
+     */
+    public void testSubmitRunnable2() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<String> future = e.submit(new NoOpRunnable(), TEST_STRING);
+            String result = future.get();
+            assertSame(TEST_STRING, result);
+        }
+    }
+
+    /**
+     * invokeAny(null) throws NPE
+     */
+    public void testInvokeAny1() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * invokeAny(empty collection) throws IAE
+     */
+    public void testInvokeAny2() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(new ArrayList<Callable<String>>());
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * invokeAny(c) throws NPE if c has null elements
+     */
+    public void testInvokeAny3() throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(latchAwaitingStringTask(latch));
+            l.add(null);
+            try {
+                e.invokeAny(l);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+            latch.countDown();
+        }
+    }
+
+    /**
+     * invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testInvokeAny4() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new NPETask());
+            try {
+                e.invokeAny(l);
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * invokeAny(c) returns result of some task
+     */
+    public void testInvokeAny5() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l);
+            assertSame(TEST_STRING, result);
+        }
+    }
+
+    /**
+     * invokeAll(null) throws NPE
+     */
+    public void testInvokeAll1() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAll(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * invokeAll(empty collection) returns empty collection
+     */
+    public void testInvokeAll2() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>());
+            assertTrue(r.isEmpty());
+        }
+    }
+
+    /**
+     * invokeAll(c) throws NPE if c has null elements
+     */
+    public void testInvokeAll3() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(null);
+            try {
+                e.invokeAll(l);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * get of element of invokeAll(c) throws exception on failed task
+     */
+    public void testInvokeAll4() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new NPETask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(1, futures.size());
+            try {
+                futures.get(0).get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * invokeAll(c) returns results of all completed tasks
+     */
+    public void testInvokeAll5() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        }
+    }
+
+    /**
+     * timed invokeAny(null) throws NPE
+     */
+    public void testTimedInvokeAny1() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(,,null) throws NPE
+     */
+    public void testTimedInvokeAnyNullTimeUnit() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            try {
+                e.invokeAny(l, MEDIUM_DELAY_MS, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(empty collection) throws IAE
+     */
+    public void testTimedInvokeAny2() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(new ArrayList<Callable<String>>(),
+                            MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAny3() throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(latchAwaitingStringTask(latch));
+            l.add(null);
+            try {
+                e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+            latch.countDown();
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testTimedInvokeAny4() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            long startTime = System.nanoTime();
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new NPETask());
+            try {
+                e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) returns result of some task
+     */
+    public void testTimedInvokeAny5() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            long startTime = System.nanoTime();
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+            assertSame(TEST_STRING, result);
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * timed invokeAll(null) throws NPE
+     */
+    public void testTimedInvokeAll1() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAll(,,null) throws NPE
+     */
+    public void testTimedInvokeAllNullTimeUnit() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            try {
+                e.invokeAll(l, MEDIUM_DELAY_MS, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAll(empty collection) returns empty collection
+     */
+    public void testTimedInvokeAll2() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>(),
+                                                 MEDIUM_DELAY_MS, MILLISECONDS);
+            assertTrue(r.isEmpty());
+        }
+    }
+
+    /**
+     * timed invokeAll(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAll3() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(null);
+            try {
+                e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * get of element of invokeAll(c) throws exception on failed task
+     */
+    public void testTimedInvokeAll4() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new NPETask());
+            List<Future<String>> futures =
+                e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
+            assertEquals(1, futures.size());
+            try {
+                futures.get(0).get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * timed invokeAll(c) returns results of all completed tasks
+     */
+    public void testTimedInvokeAll5() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures =
+                e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        }
+    }
+
+    /**
+     * timed invokeAll(c) cancels tasks not completed by timeout
+     */
+    public void testTimedInvokeAll6() throws Exception {
+        for (long timeout = timeoutMillis();;) {
+            final CountDownLatch done = new CountDownLatch(1);
+            final Callable<String> waiter = new CheckedCallable<String>() {
+                public String realCall() {
+                    try { done.await(LONG_DELAY_MS, MILLISECONDS); }
+                    catch (InterruptedException ok) {}
+                    return "1"; }};
+            final ExecutorService p =
+                new CustomTPE(2, 2,
+                              LONG_DELAY_MS, MILLISECONDS,
+                              new ArrayBlockingQueue<Runnable>(10));
+            try (PoolCleaner cleaner = cleaner(p, done)) {
+                List<Callable<String>> tasks = new ArrayList<>();
+                tasks.add(new StringTask("0"));
+                tasks.add(waiter);
+                tasks.add(new StringTask("2"));
+                long startTime = System.nanoTime();
+                List<Future<String>> futures =
+                    p.invokeAll(tasks, timeout, MILLISECONDS);
+                assertEquals(tasks.size(), futures.size());
+                assertTrue(millisElapsedSince(startTime) >= timeout);
+                for (Future future : futures)
+                    assertTrue(future.isDone());
+                assertTrue(futures.get(1).isCancelled());
+                try {
+                    assertEquals("0", futures.get(0).get());
+                    assertEquals("2", futures.get(2).get());
+                    break;
+                } catch (CancellationException retryWithLongerTimeout) {
+                    timeout *= 2;
+                    if (timeout >= LONG_DELAY_MS / 2)
+                        fail("expected exactly one task to be cancelled");
+                }
+            }
+        }
+    }
+
+    /**
+     * Execution continues if there is at least one thread even if
+     * thread factory fails to create more
+     */
+    public void testFailingThreadFactory() throws InterruptedException {
+        final ExecutorService e =
+            new CustomTPE(100, 100,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new LinkedBlockingQueue<Runnable>(),
+                          new FailingThreadFactory());
+        try (PoolCleaner cleaner = cleaner(e)) {
+            final int TASKS = 100;
+            final CountDownLatch done = new CountDownLatch(TASKS);
+            for (int k = 0; k < TASKS; ++k)
+                e.execute(new CheckedRunnable() {
+                    public void realRun() {
+                        done.countDown();
+                    }});
+            assertTrue(done.await(LONG_DELAY_MS, MILLISECONDS));
+        }
+    }
+
+    /**
+     * allowsCoreThreadTimeOut is by default false.
+     */
+    public void testAllowsCoreThreadTimeOut() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(2, 2,
+                          1000, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertFalse(p.allowsCoreThreadTimeOut());
+        }
+    }
+
+    /**
+     * allowCoreThreadTimeOut(true) causes idle threads to time out
+     */
+    public void testAllowCoreThreadTimeOut_true() throws Exception {
+        long keepAliveTime = timeoutMillis();
+        final ThreadPoolExecutor p =
+            new CustomTPE(2, 10,
+                          keepAliveTime, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            p.allowCoreThreadTimeOut(true);
+            p.execute(new CheckedRunnable() {
+                public void realRun() {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getPoolSize());
+                }});
+            await(threadStarted);
+            delay(keepAliveTime);
+            long startTime = System.nanoTime();
+            while (p.getPoolSize() > 0
+                   && millisElapsedSince(startTime) < LONG_DELAY_MS)
+                Thread.yield();
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+            assertEquals(0, p.getPoolSize());
+        }
+    }
+
+    /**
+     * allowCoreThreadTimeOut(false) causes idle threads not to time out
+     */
+    public void testAllowCoreThreadTimeOut_false() throws Exception {
+        long keepAliveTime = timeoutMillis();
+        final ThreadPoolExecutor p =
+            new CustomTPE(2, 10,
+                          keepAliveTime, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            p.allowCoreThreadTimeOut(false);
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertTrue(p.getPoolSize() >= 1);
+                }});
+            delay(2 * keepAliveTime);
+            assertTrue(p.getPoolSize() >= 1);
+        }
+    }
+
+    /**
+     * get(cancelled task) throws CancellationException
+     * (in part, a test of CustomTPE itself)
+     */
+    public void testGet_cancelled() throws Exception {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ExecutorService e =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new LinkedBlockingQueue<Runnable>());
+        try (PoolCleaner cleaner = cleaner(e, done)) {
+            final CountDownLatch blockerStarted = new CountDownLatch(1);
+            final List<Future<?>> futures = new ArrayList<>();
+            for (int i = 0; i < 2; i++) {
+                Runnable r = new CheckedRunnable() { public void realRun()
+                                                         throws Throwable {
+                    blockerStarted.countDown();
+                    assertTrue(done.await(2 * LONG_DELAY_MS, MILLISECONDS));
+                }};
+                futures.add(e.submit(r));
+            }
+            await(blockerStarted);
+            for (Future<?> future : futures) future.cancel(false);
+            for (Future<?> future : futures) {
+                try {
+                    future.get();
+                    shouldThrow();
+                } catch (CancellationException success) {}
+                try {
+                    future.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (CancellationException success) {}
+                assertTrue(future.isCancelled());
+                assertTrue(future.isDone());
+            }
+        }
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/ThreadPoolExecutorTest.java b/jdk/test/java/util/concurrent/tck/ThreadPoolExecutorTest.java
new file mode 100644
index 00000000000..5df3c94b120
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/ThreadPoolExecutorTest.java
@@ -0,0 +1,2093 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ThreadPoolExecutorTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ThreadPoolExecutorTest.class);
+    }
+
+    static class ExtendedTPE extends ThreadPoolExecutor {
+        final CountDownLatch beforeCalled = new CountDownLatch(1);
+        final CountDownLatch afterCalled = new CountDownLatch(1);
+        final CountDownLatch terminatedCalled = new CountDownLatch(1);
+
+        public ExtendedTPE() {
+            super(1, 1, LONG_DELAY_MS, MILLISECONDS, new SynchronousQueue<Runnable>());
+        }
+        protected void beforeExecute(Thread t, Runnable r) {
+            beforeCalled.countDown();
+        }
+        protected void afterExecute(Runnable r, Throwable t) {
+            afterCalled.countDown();
+        }
+        protected void terminated() {
+            terminatedCalled.countDown();
+        }
+
+        public boolean beforeCalled() {
+            return beforeCalled.getCount() == 0;
+        }
+        public boolean afterCalled() {
+            return afterCalled.getCount() == 0;
+        }
+        public boolean terminatedCalled() {
+            return terminatedCalled.getCount() == 0;
+        }
+    }
+
+    static class FailingThreadFactory implements ThreadFactory {
+        int calls = 0;
+        public Thread newThread(Runnable r) {
+            if (++calls > 1) return null;
+            return new Thread(r);
+        }
+    }
+
+    /**
+     * execute successfully executes a runnable
+     */
+    public void testExecute() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch done = new CountDownLatch(1);
+            final Runnable task = new CheckedRunnable() {
+                public void realRun() { done.countDown(); }};
+            p.execute(task);
+            assertTrue(done.await(LONG_DELAY_MS, MILLISECONDS));
+        }
+    }
+
+    /**
+     * getActiveCount increases but doesn't overestimate, when a
+     * thread becomes active
+     */
+    public void testGetActiveCount() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            assertEquals(0, p.getActiveCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getActiveCount());
+                    await(done);
+                }});
+            await(threadStarted);
+            assertEquals(1, p.getActiveCount());
+        }
+    }
+
+    /**
+     * prestartCoreThread starts a thread if under corePoolSize, else doesn't
+     */
+    public void testPrestartCoreThread() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 6,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertEquals(0, p.getPoolSize());
+            assertTrue(p.prestartCoreThread());
+            assertEquals(1, p.getPoolSize());
+            assertTrue(p.prestartCoreThread());
+            assertEquals(2, p.getPoolSize());
+            assertFalse(p.prestartCoreThread());
+            assertEquals(2, p.getPoolSize());
+            p.setCorePoolSize(4);
+            assertTrue(p.prestartCoreThread());
+            assertEquals(3, p.getPoolSize());
+            assertTrue(p.prestartCoreThread());
+            assertEquals(4, p.getPoolSize());
+            assertFalse(p.prestartCoreThread());
+            assertEquals(4, p.getPoolSize());
+        }
+    }
+
+    /**
+     * prestartAllCoreThreads starts all corePoolSize threads
+     */
+    public void testPrestartAllCoreThreads() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 6,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertEquals(0, p.getPoolSize());
+            p.prestartAllCoreThreads();
+            assertEquals(2, p.getPoolSize());
+            p.prestartAllCoreThreads();
+            assertEquals(2, p.getPoolSize());
+            p.setCorePoolSize(4);
+            p.prestartAllCoreThreads();
+            assertEquals(4, p.getPoolSize());
+            p.prestartAllCoreThreads();
+            assertEquals(4, p.getPoolSize());
+        }
+    }
+
+    /**
+     * getCompletedTaskCount increases, but doesn't overestimate,
+     * when tasks complete
+     */
+    public void testGetCompletedTaskCount() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            final CountDownLatch threadProceed = new CountDownLatch(1);
+            final CountDownLatch threadDone = new CountDownLatch(1);
+            assertEquals(0, p.getCompletedTaskCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(0, p.getCompletedTaskCount());
+                    threadProceed.await();
+                    threadDone.countDown();
+                }});
+            await(threadStarted);
+            assertEquals(0, p.getCompletedTaskCount());
+            threadProceed.countDown();
+            threadDone.await();
+            long startTime = System.nanoTime();
+            while (p.getCompletedTaskCount() != 1) {
+                if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                    fail("timed out");
+                Thread.yield();
+            }
+        }
+    }
+
+    /**
+     * getCorePoolSize returns size given in constructor if not otherwise set
+     */
+    public void testGetCorePoolSize() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertEquals(1, p.getCorePoolSize());
+        }
+    }
+
+    /**
+     * getKeepAliveTime returns value given in constructor if not otherwise set
+     */
+    public void testGetKeepAliveTime() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 2,
+                                   1000, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertEquals(1, p.getKeepAliveTime(SECONDS));
+        }
+    }
+
+    /**
+     * getThreadFactory returns factory in constructor if not set
+     */
+    public void testGetThreadFactory() {
+        ThreadFactory threadFactory = new SimpleThreadFactory();
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   threadFactory,
+                                   new NoOpREHandler());
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertSame(threadFactory, p.getThreadFactory());
+        }
+    }
+
+    /**
+     * setThreadFactory sets the thread factory returned by getThreadFactory
+     */
+    public void testSetThreadFactory() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            ThreadFactory threadFactory = new SimpleThreadFactory();
+            p.setThreadFactory(threadFactory);
+            assertSame(threadFactory, p.getThreadFactory());
+        }
+    }
+
+    /**
+     * setThreadFactory(null) throws NPE
+     */
+    public void testSetThreadFactoryNull() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.setThreadFactory(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * getRejectedExecutionHandler returns handler in constructor if not set
+     */
+    public void testGetRejectedExecutionHandler() {
+        final RejectedExecutionHandler handler = new NoOpREHandler();
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   handler);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertSame(handler, p.getRejectedExecutionHandler());
+        }
+    }
+
+    /**
+     * setRejectedExecutionHandler sets the handler returned by
+     * getRejectedExecutionHandler
+     */
+    public void testSetRejectedExecutionHandler() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            RejectedExecutionHandler handler = new NoOpREHandler();
+            p.setRejectedExecutionHandler(handler);
+            assertSame(handler, p.getRejectedExecutionHandler());
+        }
+    }
+
+    /**
+     * setRejectedExecutionHandler(null) throws NPE
+     */
+    public void testSetRejectedExecutionHandlerNull() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.setRejectedExecutionHandler(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * getLargestPoolSize increases, but doesn't overestimate, when
+     * multiple threads active
+     */
+    public void testGetLargestPoolSize() throws InterruptedException {
+        final int THREADS = 3;
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(THREADS, THREADS,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            assertEquals(0, p.getLargestPoolSize());
+            final CountDownLatch threadsStarted = new CountDownLatch(THREADS);
+            for (int i = 0; i < THREADS; i++)
+                p.execute(new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadsStarted.countDown();
+                        await(done);
+                        assertEquals(THREADS, p.getLargestPoolSize());
+                    }});
+            await(threadsStarted);
+            assertEquals(THREADS, p.getLargestPoolSize());
+        }
+        assertEquals(THREADS, p.getLargestPoolSize());
+    }
+
+    /**
+     * getMaximumPoolSize returns value given in constructor if not
+     * otherwise set
+     */
+    public void testGetMaximumPoolSize() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 3,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertEquals(3, p.getMaximumPoolSize());
+            p.setMaximumPoolSize(5);
+            assertEquals(5, p.getMaximumPoolSize());
+            p.setMaximumPoolSize(4);
+            assertEquals(4, p.getMaximumPoolSize());
+        }
+    }
+
+    /**
+     * getPoolSize increases, but doesn't overestimate, when threads
+     * become active
+     */
+    public void testGetPoolSize() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            assertEquals(0, p.getPoolSize());
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getPoolSize());
+                    await(done);
+                }});
+            await(threadStarted);
+            assertEquals(1, p.getPoolSize());
+        }
+    }
+
+    /**
+     * getTaskCount increases, but doesn't overestimate, when tasks submitted
+     */
+    public void testGetTaskCount() throws InterruptedException {
+        final int TASKS = 3;
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            assertEquals(0, p.getTaskCount());
+            assertEquals(0, p.getCompletedTaskCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    await(done);
+                }});
+            await(threadStarted);
+            assertEquals(1, p.getTaskCount());
+            assertEquals(0, p.getCompletedTaskCount());
+            for (int i = 0; i < TASKS; i++) {
+                assertEquals(1 + i, p.getTaskCount());
+                p.execute(new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadStarted.countDown();
+                        assertEquals(1 + TASKS, p.getTaskCount());
+                        await(done);
+                    }});
+            }
+            assertEquals(1 + TASKS, p.getTaskCount());
+            assertEquals(0, p.getCompletedTaskCount());
+        }
+        assertEquals(1 + TASKS, p.getTaskCount());
+        assertEquals(1 + TASKS, p.getCompletedTaskCount());
+    }
+
+    /**
+     * isShutdown is false before shutdown, true after
+     */
+    public void testIsShutdown() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertFalse(p.isShutdown());
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertTrue(p.isShutdown());
+        }
+    }
+
+    /**
+     * awaitTermination on a non-shutdown pool times out
+     */
+    public void testAwaitTermination_timesOut() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertFalse(p.isTerminated());
+            assertFalse(p.awaitTermination(Long.MIN_VALUE, NANOSECONDS));
+            assertFalse(p.awaitTermination(Long.MIN_VALUE, MILLISECONDS));
+            assertFalse(p.awaitTermination(-1L, NANOSECONDS));
+            assertFalse(p.awaitTermination(-1L, MILLISECONDS));
+            assertFalse(p.awaitTermination(0L, NANOSECONDS));
+            assertFalse(p.awaitTermination(0L, MILLISECONDS));
+            long timeoutNanos = 999999L;
+            long startTime = System.nanoTime();
+            assertFalse(p.awaitTermination(timeoutNanos, NANOSECONDS));
+            assertTrue(System.nanoTime() - startTime >= timeoutNanos);
+            assertFalse(p.isTerminated());
+            startTime = System.nanoTime();
+            long timeoutMillis = timeoutMillis();
+            assertFalse(p.awaitTermination(timeoutMillis, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+            assertFalse(p.isTerminated());
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(p.isTerminated());
+        }
+    }
+
+    /**
+     * isTerminated is false before termination, true after
+     */
+    public void testIsTerminated() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            final CountDownLatch done = new CountDownLatch(1);
+            assertFalse(p.isTerminating());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(p.isTerminating());
+                    threadStarted.countDown();
+                    await(done);
+                }});
+            await(threadStarted);
+            assertFalse(p.isTerminating());
+            done.countDown();
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(p.isTerminated());
+            assertFalse(p.isTerminating());
+        }
+    }
+
+    /**
+     * isTerminating is not true when running or when terminated
+     */
+    public void testIsTerminating() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            final CountDownLatch done = new CountDownLatch(1);
+            assertFalse(p.isTerminating());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(p.isTerminating());
+                    threadStarted.countDown();
+                    await(done);
+                }});
+            await(threadStarted);
+            assertFalse(p.isTerminating());
+            done.countDown();
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(p.isTerminated());
+            assertFalse(p.isTerminating());
+        }
+    }
+
+    /**
+     * getQueue returns the work queue, which contains queued tasks
+     */
+    public void testGetQueue() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final BlockingQueue<Runnable> q = new ArrayBlockingQueue<Runnable>(10);
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   q);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            FutureTask[] tasks = new FutureTask[5];
+            for (int i = 0; i < tasks.length; i++) {
+                Callable task = new CheckedCallable<Boolean>() {
+                    public Boolean realCall() throws InterruptedException {
+                        threadStarted.countDown();
+                        assertSame(q, p.getQueue());
+                        await(done);
+                        return Boolean.TRUE;
+                    }};
+                tasks[i] = new FutureTask(task);
+                p.execute(tasks[i]);
+            }
+            await(threadStarted);
+            assertSame(q, p.getQueue());
+            assertFalse(q.contains(tasks[0]));
+            assertTrue(q.contains(tasks[tasks.length - 1]));
+            assertEquals(tasks.length - 1, q.size());
+        }
+    }
+
+    /**
+     * remove(task) removes queued task, and fails to remove active task
+     */
+    public void testRemove() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        BlockingQueue<Runnable> q = new ArrayBlockingQueue<Runnable>(10);
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   q);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            Runnable[] tasks = new Runnable[6];
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            for (int i = 0; i < tasks.length; i++) {
+                tasks[i] = new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadStarted.countDown();
+                        await(done);
+                    }};
+                p.execute(tasks[i]);
+            }
+            await(threadStarted);
+            assertFalse(p.remove(tasks[0]));
+            assertTrue(q.contains(tasks[4]));
+            assertTrue(q.contains(tasks[3]));
+            assertTrue(p.remove(tasks[4]));
+            assertFalse(p.remove(tasks[4]));
+            assertFalse(q.contains(tasks[4]));
+            assertTrue(q.contains(tasks[3]));
+            assertTrue(p.remove(tasks[3]));
+            assertFalse(q.contains(tasks[3]));
+        }
+    }
+
+    /**
+     * purge removes cancelled tasks from the queue
+     */
+    public void testPurge() throws InterruptedException {
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        final BlockingQueue<Runnable> q = new ArrayBlockingQueue<Runnable>(10);
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   q);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            FutureTask[] tasks = new FutureTask[5];
+            for (int i = 0; i < tasks.length; i++) {
+                Callable task = new CheckedCallable<Boolean>() {
+                    public Boolean realCall() throws InterruptedException {
+                        threadStarted.countDown();
+                        await(done);
+                        return Boolean.TRUE;
+                    }};
+                tasks[i] = new FutureTask(task);
+                p.execute(tasks[i]);
+            }
+            await(threadStarted);
+            assertEquals(tasks.length, p.getTaskCount());
+            assertEquals(tasks.length - 1, q.size());
+            assertEquals(1L, p.getActiveCount());
+            assertEquals(0L, p.getCompletedTaskCount());
+            tasks[4].cancel(true);
+            tasks[3].cancel(false);
+            p.purge();
+            assertEquals(tasks.length - 3, q.size());
+            assertEquals(tasks.length - 2, p.getTaskCount());
+            p.purge();         // Nothing to do
+            assertEquals(tasks.length - 3, q.size());
+            assertEquals(tasks.length - 2, p.getTaskCount());
+        }
+    }
+
+    /**
+     * shutdownNow returns a list containing tasks that were not run,
+     * and those tasks are drained from the queue
+     */
+    public void testShutdownNow() throws InterruptedException {
+        final int poolSize = 2;
+        final int count = 5;
+        final AtomicInteger ran = new AtomicInteger(0);
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(poolSize, poolSize,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        final CountDownLatch threadsStarted = new CountDownLatch(poolSize);
+        Runnable waiter = new CheckedRunnable() { public void realRun() {
+            threadsStarted.countDown();
+            try {
+                MILLISECONDS.sleep(2 * LONG_DELAY_MS);
+            } catch (InterruptedException success) {}
+            ran.getAndIncrement();
+        }};
+        for (int i = 0; i < count; i++)
+            p.execute(waiter);
+        await(threadsStarted);
+        assertEquals(poolSize, p.getActiveCount());
+        assertEquals(0, p.getCompletedTaskCount());
+        final List<Runnable> queuedTasks;
+        try {
+            queuedTasks = p.shutdownNow();
+        } catch (SecurityException ok) {
+            return; // Allowed in case test doesn't have privs
+        }
+        assertTrue(p.isShutdown());
+        assertTrue(p.getQueue().isEmpty());
+        assertEquals(count - poolSize, queuedTasks.size());
+        assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+        assertEquals(poolSize, ran.get());
+        assertEquals(poolSize, p.getCompletedTaskCount());
+    }
+
+    // Exception Tests
+
+    /**
+     * Constructor throws if corePoolSize argument is less than zero
+     */
+    public void testConstructor1() {
+        try {
+            new ThreadPoolExecutor(-1, 1, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is less than zero
+     */
+    public void testConstructor2() {
+        try {
+            new ThreadPoolExecutor(1, -1, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is equal to zero
+     */
+    public void testConstructor3() {
+        try {
+            new ThreadPoolExecutor(1, 0, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if keepAliveTime is less than zero
+     */
+    public void testConstructor4() {
+        try {
+            new ThreadPoolExecutor(1, 2, -1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize is greater than the maximumPoolSize
+     */
+    public void testConstructor5() {
+        try {
+            new ThreadPoolExecutor(2, 1, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if workQueue is set to null
+     */
+    public void testConstructorNullPointerException() {
+        try {
+            new ThreadPoolExecutor(1, 2, 1L, SECONDS,
+                                   (BlockingQueue) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize argument is less than zero
+     */
+    public void testConstructor6() {
+        try {
+            new ThreadPoolExecutor(-1, 1, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is less than zero
+     */
+    public void testConstructor7() {
+        try {
+            new ThreadPoolExecutor(1, -1, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is equal to zero
+     */
+    public void testConstructor8() {
+        try {
+            new ThreadPoolExecutor(1, 0, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if keepAliveTime is less than zero
+     */
+    public void testConstructor9() {
+        try {
+            new ThreadPoolExecutor(1, 2, -1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize is greater than the maximumPoolSize
+     */
+    public void testConstructor10() {
+        try {
+            new ThreadPoolExecutor(2, 1, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if workQueue is set to null
+     */
+    public void testConstructorNullPointerException2() {
+        try {
+            new ThreadPoolExecutor(1, 2, 1L, SECONDS,
+                                   (BlockingQueue) null,
+                                   new SimpleThreadFactory());
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if threadFactory is set to null
+     */
+    public void testConstructorNullPointerException3() {
+        try {
+            new ThreadPoolExecutor(1, 2, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   (ThreadFactory) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize argument is less than zero
+     */
+    public void testConstructor11() {
+        try {
+            new ThreadPoolExecutor(-1, 1, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is less than zero
+     */
+    public void testConstructor12() {
+        try {
+            new ThreadPoolExecutor(1, -1, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is equal to zero
+     */
+    public void testConstructor13() {
+        try {
+            new ThreadPoolExecutor(1, 0, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if keepAliveTime is less than zero
+     */
+    public void testConstructor14() {
+        try {
+            new ThreadPoolExecutor(1, 2, -1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize is greater than the maximumPoolSize
+     */
+    public void testConstructor15() {
+        try {
+            new ThreadPoolExecutor(2, 1, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if workQueue is set to null
+     */
+    public void testConstructorNullPointerException4() {
+        try {
+            new ThreadPoolExecutor(1, 2, 1L, SECONDS,
+                                   (BlockingQueue) null,
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if handler is set to null
+     */
+    public void testConstructorNullPointerException5() {
+        try {
+            new ThreadPoolExecutor(1, 2, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   (RejectedExecutionHandler) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize argument is less than zero
+     */
+    public void testConstructor16() {
+        try {
+            new ThreadPoolExecutor(-1, 1, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory(),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is less than zero
+     */
+    public void testConstructor17() {
+        try {
+            new ThreadPoolExecutor(1, -1, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory(),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is equal to zero
+     */
+    public void testConstructor18() {
+        try {
+            new ThreadPoolExecutor(1, 0, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory(),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if keepAliveTime is less than zero
+     */
+    public void testConstructor19() {
+        try {
+            new ThreadPoolExecutor(1, 2, -1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory(),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize is greater than the maximumPoolSize
+     */
+    public void testConstructor20() {
+        try {
+            new ThreadPoolExecutor(2, 1, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory(),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if workQueue is null
+     */
+    public void testConstructorNullPointerException6() {
+        try {
+            new ThreadPoolExecutor(1, 2, 1L, SECONDS,
+                                   (BlockingQueue) null,
+                                   new SimpleThreadFactory(),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if handler is null
+     */
+    public void testConstructorNullPointerException7() {
+        try {
+            new ThreadPoolExecutor(1, 2, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory(),
+                                   (RejectedExecutionHandler) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if ThreadFactory is null
+     */
+    public void testConstructorNullPointerException8() {
+        try {
+            new ThreadPoolExecutor(1, 2, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   (ThreadFactory) null,
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * get of submitted callable throws InterruptedException if interrupted
+     */
+    public void testInterruptedSubmit() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   60, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+                public void realRun() throws Exception {
+                    Callable task = new CheckedCallable<Boolean>() {
+                        public Boolean realCall() throws InterruptedException {
+                            threadStarted.countDown();
+                            await(done);
+                            return Boolean.TRUE;
+                        }};
+                    p.submit(task).get();
+                }});
+
+            await(threadStarted);
+            t.interrupt();
+            awaitTermination(t);
+        }
+    }
+
+    /**
+     * execute throws RejectedExecutionException if saturated.
+     */
+    public void testSaturatedExecute() {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1));
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            Runnable task = new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    await(done);
+                }};
+            for (int i = 0; i < 2; ++i)
+                p.execute(task);
+            for (int i = 0; i < 2; ++i) {
+                try {
+                    p.execute(task);
+                    shouldThrow();
+                } catch (RejectedExecutionException success) {}
+                assertTrue(p.getTaskCount() <= 2);
+            }
+        }
+    }
+
+    /**
+     * submit(runnable) throws RejectedExecutionException if saturated.
+     */
+    public void testSaturatedSubmitRunnable() {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1));
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            Runnable task = new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    await(done);
+                }};
+            for (int i = 0; i < 2; ++i)
+                p.submit(task);
+            for (int i = 0; i < 2; ++i) {
+                try {
+                    p.execute(task);
+                    shouldThrow();
+                } catch (RejectedExecutionException success) {}
+                assertTrue(p.getTaskCount() <= 2);
+            }
+        }
+    }
+
+    /**
+     * submit(callable) throws RejectedExecutionException if saturated.
+     */
+    public void testSaturatedSubmitCallable() {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1));
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            Runnable task = new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    await(done);
+                }};
+            for (int i = 0; i < 2; ++i)
+                p.submit(Executors.callable(task));
+            for (int i = 0; i < 2; ++i) {
+                try {
+                    p.execute(task);
+                    shouldThrow();
+                } catch (RejectedExecutionException success) {}
+                assertTrue(p.getTaskCount() <= 2);
+            }
+        }
+    }
+
+    /**
+     * executor using CallerRunsPolicy runs task if saturated.
+     */
+    public void testSaturatedExecute2() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS,
+                                   MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1),
+                                   new ThreadPoolExecutor.CallerRunsPolicy());
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch done = new CountDownLatch(1);
+            Runnable blocker = new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    await(done);
+                }};
+            p.execute(blocker);
+            TrackedNoOpRunnable[] tasks = new TrackedNoOpRunnable[5];
+            for (int i = 0; i < tasks.length; i++)
+                tasks[i] = new TrackedNoOpRunnable();
+            for (int i = 0; i < tasks.length; i++)
+                p.execute(tasks[i]);
+            for (int i = 1; i < tasks.length; i++)
+                assertTrue(tasks[i].done);
+            assertFalse(tasks[0].done); // waiting in queue
+            done.countDown();
+        }
+    }
+
+    /**
+     * executor using DiscardPolicy drops task if saturated.
+     */
+    public void testSaturatedExecute3() {
+        final CountDownLatch done = new CountDownLatch(1);
+        final TrackedNoOpRunnable[] tasks = new TrackedNoOpRunnable[5];
+        for (int i = 0; i < tasks.length; ++i)
+            tasks[i] = new TrackedNoOpRunnable();
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(1),
+                          new ThreadPoolExecutor.DiscardPolicy());
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            p.execute(awaiter(done));
+
+            for (TrackedNoOpRunnable task : tasks)
+                p.execute(task);
+            for (int i = 1; i < tasks.length; i++)
+                assertFalse(tasks[i].done);
+        }
+        for (int i = 1; i < tasks.length; i++)
+            assertFalse(tasks[i].done);
+        assertTrue(tasks[0].done); // was waiting in queue
+    }
+
+    /**
+     * executor using DiscardOldestPolicy drops oldest task if saturated.
+     */
+    public void testSaturatedExecute4() {
+        final CountDownLatch done = new CountDownLatch(1);
+        LatchAwaiter r1 = awaiter(done);
+        LatchAwaiter r2 = awaiter(done);
+        LatchAwaiter r3 = awaiter(done);
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1),
+                                   new ThreadPoolExecutor.DiscardOldestPolicy());
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            assertEquals(LatchAwaiter.NEW, r1.state);
+            assertEquals(LatchAwaiter.NEW, r2.state);
+            assertEquals(LatchAwaiter.NEW, r3.state);
+            p.execute(r1);
+            p.execute(r2);
+            assertTrue(p.getQueue().contains(r2));
+            p.execute(r3);
+            assertFalse(p.getQueue().contains(r2));
+            assertTrue(p.getQueue().contains(r3));
+        }
+        assertEquals(LatchAwaiter.DONE, r1.state);
+        assertEquals(LatchAwaiter.NEW, r2.state);
+        assertEquals(LatchAwaiter.DONE, r3.state);
+    }
+
+    /**
+     * execute throws RejectedExecutionException if shutdown
+     */
+    public void testRejectedExecutionExceptionOnShutdown() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1));
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.execute(new NoOpRunnable());
+                shouldThrow();
+            } catch (RejectedExecutionException success) {}
+        }
+    }
+
+    /**
+     * execute using CallerRunsPolicy drops task on shutdown
+     */
+    public void testCallerRunsOnShutdown() {
+        RejectedExecutionHandler h = new ThreadPoolExecutor.CallerRunsPolicy();
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1), h);
+
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        try (PoolCleaner cleaner = cleaner(p)) {
+            TrackedNoOpRunnable r = new TrackedNoOpRunnable();
+            p.execute(r);
+            assertFalse(r.done);
+        }
+    }
+
+    /**
+     * execute using DiscardPolicy drops task on shutdown
+     */
+    public void testDiscardOnShutdown() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1),
+                                   new ThreadPoolExecutor.DiscardPolicy());
+
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        try (PoolCleaner cleaner = cleaner(p)) {
+            TrackedNoOpRunnable r = new TrackedNoOpRunnable();
+            p.execute(r);
+            assertFalse(r.done);
+        }
+    }
+
+    /**
+     * execute using DiscardOldestPolicy drops task on shutdown
+     */
+    public void testDiscardOldestOnShutdown() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1),
+                                   new ThreadPoolExecutor.DiscardOldestPolicy());
+
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        try (PoolCleaner cleaner = cleaner(p)) {
+            TrackedNoOpRunnable r = new TrackedNoOpRunnable();
+            p.execute(r);
+            assertFalse(r.done);
+        }
+    }
+
+    /**
+     * execute(null) throws NPE
+     */
+    public void testExecuteNull() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 2,
+                                   1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.execute(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * setCorePoolSize of negative value throws IllegalArgumentException
+     */
+    public void testCorePoolSizeIllegalArgumentException() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.setCorePoolSize(-1);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * setMaximumPoolSize(int) throws IllegalArgumentException if
+     * given a value less the core pool size
+     */
+    public void testMaximumPoolSizeIllegalArgumentException() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 3,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.setMaximumPoolSize(1);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * setMaximumPoolSize throws IllegalArgumentException
+     * if given a negative value
+     */
+    public void testMaximumPoolSizeIllegalArgumentException2() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 3,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.setMaximumPoolSize(-1);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * Configuration changes that allow core pool size greater than
+     * max pool size result in IllegalArgumentException.
+     */
+    public void testPoolSizeInvariants() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            for (int s = 1; s < 5; s++) {
+                p.setMaximumPoolSize(s);
+                p.setCorePoolSize(s);
+                try {
+                    p.setMaximumPoolSize(s - 1);
+                    shouldThrow();
+                } catch (IllegalArgumentException success) {}
+                assertEquals(s, p.getCorePoolSize());
+                assertEquals(s, p.getMaximumPoolSize());
+                try {
+                    p.setCorePoolSize(s + 1);
+                    shouldThrow();
+                } catch (IllegalArgumentException success) {}
+                assertEquals(s, p.getCorePoolSize());
+                assertEquals(s, p.getMaximumPoolSize());
+            }
+        }
+    }
+
+    /**
+     * setKeepAliveTime throws IllegalArgumentException
+     * when given a negative value
+     */
+    public void testKeepAliveTimeIllegalArgumentException() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 3,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.setKeepAliveTime(-1, MILLISECONDS);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * terminated() is called on termination
+     */
+    public void testTerminated() {
+        ExtendedTPE p = new ExtendedTPE();
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertTrue(p.terminatedCalled());
+            assertTrue(p.isShutdown());
+        }
+    }
+
+    /**
+     * beforeExecute and afterExecute are called when executing task
+     */
+    public void testBeforeAfter() throws InterruptedException {
+        ExtendedTPE p = new ExtendedTPE();
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch done = new CountDownLatch(1);
+            p.execute(new CheckedRunnable() {
+                public void realRun() {
+                    done.countDown();
+                }});
+            await(p.afterCalled);
+            assertEquals(0, done.getCount());
+            assertTrue(p.afterCalled());
+            assertTrue(p.beforeCalled());
+        }
+    }
+
+    /**
+     * completed submit of callable returns result
+     */
+    public void testSubmitCallable() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<String> future = e.submit(new StringTask());
+            String result = future.get();
+            assertSame(TEST_STRING, result);
+        }
+    }
+
+    /**
+     * completed submit of runnable returns successfully
+     */
+    public void testSubmitRunnable() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<?> future = e.submit(new NoOpRunnable());
+            future.get();
+            assertTrue(future.isDone());
+        }
+    }
+
+    /**
+     * completed submit of (runnable, result) returns result
+     */
+    public void testSubmitRunnable2() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<String> future = e.submit(new NoOpRunnable(), TEST_STRING);
+            String result = future.get();
+            assertSame(TEST_STRING, result);
+        }
+    }
+
+    /**
+     * invokeAny(null) throws NPE
+     */
+    public void testInvokeAny1() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * invokeAny(empty collection) throws IAE
+     */
+    public void testInvokeAny2() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(new ArrayList<Callable<String>>());
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * invokeAny(c) throws NPE if c has null elements
+     */
+    public void testInvokeAny3() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(latchAwaitingStringTask(latch));
+            l.add(null);
+            try {
+                e.invokeAny(l);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+            latch.countDown();
+        }
+    }
+
+    /**
+     * invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testInvokeAny4() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new NPETask());
+            try {
+                e.invokeAny(l);
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * invokeAny(c) returns result of some task
+     */
+    public void testInvokeAny5() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l);
+            assertSame(TEST_STRING, result);
+        }
+    }
+
+    /**
+     * invokeAll(null) throws NPE
+     */
+    public void testInvokeAll1() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAll(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * invokeAll(empty collection) returns empty collection
+     */
+    public void testInvokeAll2() throws InterruptedException {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>());
+            assertTrue(r.isEmpty());
+        }
+    }
+
+    /**
+     * invokeAll(c) throws NPE if c has null elements
+     */
+    public void testInvokeAll3() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(null);
+            try {
+                e.invokeAll(l);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * get of element of invokeAll(c) throws exception on failed task
+     */
+    public void testInvokeAll4() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new NPETask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(1, futures.size());
+            try {
+                futures.get(0).get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * invokeAll(c) returns results of all completed tasks
+     */
+    public void testInvokeAll5() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        }
+    }
+
+    /**
+     * timed invokeAny(null) throws NPE
+     */
+    public void testTimedInvokeAny1() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(,,null) throws NPE
+     */
+    public void testTimedInvokeAnyNullTimeUnit() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            try {
+                e.invokeAny(l, MEDIUM_DELAY_MS, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(empty collection) throws IAE
+     */
+    public void testTimedInvokeAny2() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(new ArrayList<Callable<String>>(),
+                            MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAny3() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(latchAwaitingStringTask(latch));
+            l.add(null);
+            try {
+                e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+            latch.countDown();
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testTimedInvokeAny4() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            long startTime = System.nanoTime();
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new NPETask());
+            try {
+                e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) returns result of some task
+     */
+    public void testTimedInvokeAny5() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            long startTime = System.nanoTime();
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+            assertSame(TEST_STRING, result);
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * timed invokeAll(null) throws NPE
+     */
+    public void testTimedInvokeAll1() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAll(,,null) throws NPE
+     */
+    public void testTimedInvokeAllNullTimeUnit() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            try {
+                e.invokeAll(l, MEDIUM_DELAY_MS, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAll(empty collection) returns empty collection
+     */
+    public void testTimedInvokeAll2() throws InterruptedException {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>(),
+                                                 MEDIUM_DELAY_MS, MILLISECONDS);
+            assertTrue(r.isEmpty());
+        }
+    }
+
+    /**
+     * timed invokeAll(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAll3() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(null);
+            try {
+                e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * get of element of invokeAll(c) throws exception on failed task
+     */
+    public void testTimedInvokeAll4() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new NPETask());
+            List<Future<String>> futures =
+                e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
+            assertEquals(1, futures.size());
+            try {
+                futures.get(0).get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * timed invokeAll(c) returns results of all completed tasks
+     */
+    public void testTimedInvokeAll5() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<Callable<String>>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures =
+                e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        }
+    }
+
+    /**
+     * timed invokeAll(c) cancels tasks not completed by timeout
+     */
+    public void testTimedInvokeAll6() throws Exception {
+        for (long timeout = timeoutMillis();;) {
+            final CountDownLatch done = new CountDownLatch(1);
+            final Callable<String> waiter = new CheckedCallable<String>() {
+                public String realCall() {
+                    try { done.await(LONG_DELAY_MS, MILLISECONDS); }
+                    catch (InterruptedException ok) {}
+                    return "1"; }};
+            final ExecutorService p =
+                new ThreadPoolExecutor(2, 2,
+                                       LONG_DELAY_MS, MILLISECONDS,
+                                       new ArrayBlockingQueue<Runnable>(10));
+            try (PoolCleaner cleaner = cleaner(p, done)) {
+                List<Callable<String>> tasks = new ArrayList<>();
+                tasks.add(new StringTask("0"));
+                tasks.add(waiter);
+                tasks.add(new StringTask("2"));
+                long startTime = System.nanoTime();
+                List<Future<String>> futures =
+                    p.invokeAll(tasks, timeout, MILLISECONDS);
+                assertEquals(tasks.size(), futures.size());
+                assertTrue(millisElapsedSince(startTime) >= timeout);
+                for (Future future : futures)
+                    assertTrue(future.isDone());
+                assertTrue(futures.get(1).isCancelled());
+                try {
+                    assertEquals("0", futures.get(0).get());
+                    assertEquals("2", futures.get(2).get());
+                    break;
+                } catch (CancellationException retryWithLongerTimeout) {
+                    timeout *= 2;
+                    if (timeout >= LONG_DELAY_MS / 2)
+                        fail("expected exactly one task to be cancelled");
+                }
+            }
+        }
+    }
+
+    /**
+     * Execution continues if there is at least one thread even if
+     * thread factory fails to create more
+     */
+    public void testFailingThreadFactory() throws InterruptedException {
+        final ExecutorService e =
+            new ThreadPoolExecutor(100, 100,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new LinkedBlockingQueue<Runnable>(),
+                                   new FailingThreadFactory());
+        try (PoolCleaner cleaner = cleaner(e)) {
+            final int TASKS = 100;
+            final CountDownLatch done = new CountDownLatch(TASKS);
+            for (int k = 0; k < TASKS; ++k)
+                e.execute(new CheckedRunnable() {
+                    public void realRun() {
+                        done.countDown();
+                    }});
+            assertTrue(done.await(LONG_DELAY_MS, MILLISECONDS));
+        }
+    }
+
+    /**
+     * allowsCoreThreadTimeOut is by default false.
+     */
+    public void testAllowsCoreThreadTimeOut() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 2,
+                                   1000, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertFalse(p.allowsCoreThreadTimeOut());
+        }
+    }
+
+    /**
+     * allowCoreThreadTimeOut(true) causes idle threads to time out
+     */
+    public void testAllowCoreThreadTimeOut_true() throws Exception {
+        long keepAliveTime = timeoutMillis();
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 10,
+                                   keepAliveTime, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            p.allowCoreThreadTimeOut(true);
+            p.execute(new CheckedRunnable() {
+                public void realRun() {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getPoolSize());
+                }});
+            await(threadStarted);
+            delay(keepAliveTime);
+            long startTime = System.nanoTime();
+            while (p.getPoolSize() > 0
+                   && millisElapsedSince(startTime) < LONG_DELAY_MS)
+                Thread.yield();
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+            assertEquals(0, p.getPoolSize());
+        }
+    }
+
+    /**
+     * allowCoreThreadTimeOut(false) causes idle threads not to time out
+     */
+    public void testAllowCoreThreadTimeOut_false() throws Exception {
+        long keepAliveTime = timeoutMillis();
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 10,
+                                   keepAliveTime, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            p.allowCoreThreadTimeOut(false);
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertTrue(p.getPoolSize() >= 1);
+                }});
+            delay(2 * keepAliveTime);
+            assertTrue(p.getPoolSize() >= 1);
+        }
+    }
+
+    /**
+     * execute allows the same task to be submitted multiple times, even
+     * if rejected
+     */
+    public void testRejectedRecycledTask() throws InterruptedException {
+        final int nTasks = 1000;
+        final CountDownLatch done = new CountDownLatch(nTasks);
+        final Runnable recycledTask = new Runnable() {
+            public void run() {
+                done.countDown();
+            }};
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 30,
+                                   60, SECONDS,
+                                   new ArrayBlockingQueue(30));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            for (int i = 0; i < nTasks; ++i) {
+                for (;;) {
+                    try {
+                        p.execute(recycledTask);
+                        break;
+                    }
+                    catch (RejectedExecutionException ignore) {}
+                }
+            }
+            // enough time to run all tasks
+            assertTrue(done.await(nTasks * SHORT_DELAY_MS, MILLISECONDS));
+        }
+    }
+
+    /**
+     * get(cancelled task) throws CancellationException
+     */
+    public void testGet_cancelled() throws Exception {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ExecutorService e =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new LinkedBlockingQueue<Runnable>());
+        try (PoolCleaner cleaner = cleaner(e, done)) {
+            final CountDownLatch blockerStarted = new CountDownLatch(1);
+            final List<Future<?>> futures = new ArrayList<>();
+            for (int i = 0; i < 2; i++) {
+                Runnable r = new CheckedRunnable() { public void realRun()
+                                                         throws Throwable {
+                    blockerStarted.countDown();
+                    assertTrue(done.await(2 * LONG_DELAY_MS, MILLISECONDS));
+                }};
+                futures.add(e.submit(r));
+            }
+            await(blockerStarted);
+            for (Future<?> future : futures) future.cancel(false);
+            for (Future<?> future : futures) {
+                try {
+                    future.get();
+                    shouldThrow();
+                } catch (CancellationException success) {}
+                try {
+                    future.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (CancellationException success) {}
+                assertTrue(future.isCancelled());
+                assertTrue(future.isDone());
+            }
+        }
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/ThreadTest.java b/jdk/test/java/util/concurrent/tck/ThreadTest.java
new file mode 100644
index 00000000000..939d590beb9
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/ThreadTest.java
@@ -0,0 +1,100 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ThreadTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(ThreadTest.class);
+    }
+
+    static class MyHandler implements Thread.UncaughtExceptionHandler {
+        public void uncaughtException(Thread t, Throwable e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * getUncaughtExceptionHandler returns ThreadGroup unless set,
+     * otherwise returning value of last setUncaughtExceptionHandler.
+     */
+    public void testGetAndSetUncaughtExceptionHandler() {
+        // these must be done all at once to avoid state
+        // dependencies across tests
+        Thread current = Thread.currentThread();
+        ThreadGroup tg = current.getThreadGroup();
+        MyHandler eh = new MyHandler();
+        assertSame(tg, current.getUncaughtExceptionHandler());
+        current.setUncaughtExceptionHandler(eh);
+        try {
+            assertSame(eh, current.getUncaughtExceptionHandler());
+        } finally {
+            current.setUncaughtExceptionHandler(null);
+        }
+        assertSame(tg, current.getUncaughtExceptionHandler());
+    }
+
+    /**
+     * getDefaultUncaughtExceptionHandler returns value of last
+     * setDefaultUncaughtExceptionHandler.
+     */
+    public void testGetAndSetDefaultUncaughtExceptionHandler() {
+        assertEquals(null, Thread.getDefaultUncaughtExceptionHandler());
+        // failure due to securityException is OK.
+        // Would be nice to explicitly test both ways, but cannot yet.
+        Thread.UncaughtExceptionHandler defaultHandler
+            = Thread.getDefaultUncaughtExceptionHandler();
+        MyHandler eh = new MyHandler();
+        try {
+            Thread.setDefaultUncaughtExceptionHandler(eh);
+            try {
+                assertSame(eh, Thread.getDefaultUncaughtExceptionHandler());
+            } finally {
+                Thread.setDefaultUncaughtExceptionHandler(defaultHandler);
+            }
+        } catch (SecurityException ok) {
+            assertNotNull(System.getSecurityManager());
+        }
+        assertSame(defaultHandler, Thread.getDefaultUncaughtExceptionHandler());
+    }
+
+    // How to test actually using UEH within junit?
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/TimeUnitTest.java b/jdk/test/java/util/concurrent/tck/TimeUnitTest.java
new file mode 100644
index 00000000000..3b33c9a4971
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/TimeUnitTest.java
@@ -0,0 +1,463 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+import static java.util.concurrent.TimeUnit.DAYS;
+import static java.util.concurrent.TimeUnit.HOURS;
+import static java.util.concurrent.TimeUnit.MICROSECONDS;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.MINUTES;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class TimeUnitTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(TimeUnitTest.class);
+    }
+
+    // (loops to 88888 check increments at all time divisions.)
+
+    /**
+     * convert correctly converts sample values across the units
+     */
+    public void testConvert() {
+        for (long t = 0; t < 88888; ++t) {
+            assertEquals(t*60*60*24,
+                         SECONDS.convert(t, DAYS));
+            assertEquals(t*60*60,
+                         SECONDS.convert(t, HOURS));
+            assertEquals(t*60,
+                         SECONDS.convert(t, MINUTES));
+            assertEquals(t,
+                         SECONDS.convert(t, SECONDS));
+            assertEquals(t,
+                         SECONDS.convert(1000L*t, MILLISECONDS));
+            assertEquals(t,
+                         SECONDS.convert(1000000L*t, MICROSECONDS));
+            assertEquals(t,
+                         SECONDS.convert(1000000000L*t, NANOSECONDS));
+
+            assertEquals(1000L*t*60*60*24,
+                         MILLISECONDS.convert(t, DAYS));
+            assertEquals(1000L*t*60*60,
+                         MILLISECONDS.convert(t, HOURS));
+            assertEquals(1000L*t*60,
+                         MILLISECONDS.convert(t, MINUTES));
+            assertEquals(1000L*t,
+                         MILLISECONDS.convert(t, SECONDS));
+            assertEquals(t,
+                         MILLISECONDS.convert(t, MILLISECONDS));
+            assertEquals(t,
+                         MILLISECONDS.convert(1000L*t, MICROSECONDS));
+            assertEquals(t,
+                         MILLISECONDS.convert(1000000L*t, NANOSECONDS));
+
+            assertEquals(1000000L*t*60*60*24,
+                         MICROSECONDS.convert(t, DAYS));
+            assertEquals(1000000L*t*60*60,
+                         MICROSECONDS.convert(t, HOURS));
+            assertEquals(1000000L*t*60,
+                         MICROSECONDS.convert(t, MINUTES));
+            assertEquals(1000000L*t,
+                         MICROSECONDS.convert(t, SECONDS));
+            assertEquals(1000L*t,
+                         MICROSECONDS.convert(t, MILLISECONDS));
+            assertEquals(t,
+                         MICROSECONDS.convert(t, MICROSECONDS));
+            assertEquals(t,
+                         MICROSECONDS.convert(1000L*t, NANOSECONDS));
+
+            assertEquals(1000000000L*t*60*60*24,
+                         NANOSECONDS.convert(t, DAYS));
+            assertEquals(1000000000L*t*60*60,
+                         NANOSECONDS.convert(t, HOURS));
+            assertEquals(1000000000L*t*60,
+                         NANOSECONDS.convert(t, MINUTES));
+            assertEquals(1000000000L*t,
+                         NANOSECONDS.convert(t, SECONDS));
+            assertEquals(1000000L*t,
+                         NANOSECONDS.convert(t, MILLISECONDS));
+            assertEquals(1000L*t,
+                         NANOSECONDS.convert(t, MICROSECONDS));
+            assertEquals(t,
+                         NANOSECONDS.convert(t, NANOSECONDS));
+        }
+    }
+
+    /**
+     * toNanos correctly converts sample values in different units to
+     * nanoseconds
+     */
+    public void testToNanos() {
+        for (long t = 0; t < 88888; ++t) {
+            assertEquals(t*1000000000L*60*60*24,
+                         DAYS.toNanos(t));
+            assertEquals(t*1000000000L*60*60,
+                         HOURS.toNanos(t));
+            assertEquals(t*1000000000L*60,
+                         MINUTES.toNanos(t));
+            assertEquals(1000000000L*t,
+                         SECONDS.toNanos(t));
+            assertEquals(1000000L*t,
+                         MILLISECONDS.toNanos(t));
+            assertEquals(1000L*t,
+                         MICROSECONDS.toNanos(t));
+            assertEquals(t,
+                         NANOSECONDS.toNanos(t));
+        }
+    }
+
+    /**
+     * toMicros correctly converts sample values in different units to
+     * microseconds
+     */
+    public void testToMicros() {
+        for (long t = 0; t < 88888; ++t) {
+            assertEquals(t*1000000L*60*60*24,
+                         DAYS.toMicros(t));
+            assertEquals(t*1000000L*60*60,
+                         HOURS.toMicros(t));
+            assertEquals(t*1000000L*60,
+                         MINUTES.toMicros(t));
+            assertEquals(1000000L*t,
+                         SECONDS.toMicros(t));
+            assertEquals(1000L*t,
+                         MILLISECONDS.toMicros(t));
+            assertEquals(t,
+                         MICROSECONDS.toMicros(t));
+            assertEquals(t,
+                         NANOSECONDS.toMicros(t*1000L));
+        }
+    }
+
+    /**
+     * toMillis correctly converts sample values in different units to
+     * milliseconds
+     */
+    public void testToMillis() {
+        for (long t = 0; t < 88888; ++t) {
+            assertEquals(t*1000L*60*60*24,
+                         DAYS.toMillis(t));
+            assertEquals(t*1000L*60*60,
+                         HOURS.toMillis(t));
+            assertEquals(t*1000L*60,
+                         MINUTES.toMillis(t));
+            assertEquals(1000L*t,
+                         SECONDS.toMillis(t));
+            assertEquals(t,
+                         MILLISECONDS.toMillis(t));
+            assertEquals(t,
+                         MICROSECONDS.toMillis(t*1000L));
+            assertEquals(t,
+                         NANOSECONDS.toMillis(t*1000000L));
+        }
+    }
+
+    /**
+     * toSeconds correctly converts sample values in different units to
+     * seconds
+     */
+    public void testToSeconds() {
+        for (long t = 0; t < 88888; ++t) {
+            assertEquals(t*60*60*24,
+                         DAYS.toSeconds(t));
+            assertEquals(t*60*60,
+                         HOURS.toSeconds(t));
+            assertEquals(t*60,
+                         MINUTES.toSeconds(t));
+            assertEquals(t,
+                         SECONDS.toSeconds(t));
+            assertEquals(t,
+                         MILLISECONDS.toSeconds(t*1000L));
+            assertEquals(t,
+                         MICROSECONDS.toSeconds(t*1000000L));
+            assertEquals(t,
+                         NANOSECONDS.toSeconds(t*1000000000L));
+        }
+    }
+
+    /**
+     * toMinutes correctly converts sample values in different units to
+     * minutes
+     */
+    public void testToMinutes() {
+        for (long t = 0; t < 88888; ++t) {
+            assertEquals(t*60*24,
+                         DAYS.toMinutes(t));
+            assertEquals(t*60,
+                         HOURS.toMinutes(t));
+            assertEquals(t,
+                         MINUTES.toMinutes(t));
+            assertEquals(t,
+                         SECONDS.toMinutes(t*60));
+            assertEquals(t,
+                         MILLISECONDS.toMinutes(t*1000L*60));
+            assertEquals(t,
+                         MICROSECONDS.toMinutes(t*1000000L*60));
+            assertEquals(t,
+                         NANOSECONDS.toMinutes(t*1000000000L*60));
+        }
+    }
+
+    /**
+     * toHours correctly converts sample values in different units to
+     * hours
+     */
+    public void testToHours() {
+        for (long t = 0; t < 88888; ++t) {
+            assertEquals(t*24,
+                         DAYS.toHours(t));
+            assertEquals(t,
+                         HOURS.toHours(t));
+            assertEquals(t,
+                         MINUTES.toHours(t*60));
+            assertEquals(t,
+                         SECONDS.toHours(t*60*60));
+            assertEquals(t,
+                         MILLISECONDS.toHours(t*1000L*60*60));
+            assertEquals(t,
+                         MICROSECONDS.toHours(t*1000000L*60*60));
+            assertEquals(t,
+                         NANOSECONDS.toHours(t*1000000000L*60*60));
+        }
+    }
+
+    /**
+     * toDays correctly converts sample values in different units to
+     * days
+     */
+    public void testToDays() {
+        for (long t = 0; t < 88888; ++t) {
+            assertEquals(t,
+                         DAYS.toDays(t));
+            assertEquals(t,
+                         HOURS.toDays(t*24));
+            assertEquals(t,
+                         MINUTES.toDays(t*60*24));
+            assertEquals(t,
+                         SECONDS.toDays(t*60*60*24));
+            assertEquals(t,
+                         MILLISECONDS.toDays(t*1000L*60*60*24));
+            assertEquals(t,
+                         MICROSECONDS.toDays(t*1000000L*60*60*24));
+            assertEquals(t,
+                         NANOSECONDS.toDays(t*1000000000L*60*60*24));
+        }
+    }
+
+    /**
+     * convert saturates positive too-large values to Long.MAX_VALUE
+     * and negative to LONG.MIN_VALUE
+     */
+    public void testConvertSaturate() {
+        assertEquals(Long.MAX_VALUE,
+                     NANOSECONDS.convert(Long.MAX_VALUE / 2, SECONDS));
+        assertEquals(Long.MIN_VALUE,
+                     NANOSECONDS.convert(-Long.MAX_VALUE / 4, SECONDS));
+        assertEquals(Long.MAX_VALUE,
+                     NANOSECONDS.convert(Long.MAX_VALUE / 2, MINUTES));
+        assertEquals(Long.MIN_VALUE,
+                     NANOSECONDS.convert(-Long.MAX_VALUE / 4, MINUTES));
+        assertEquals(Long.MAX_VALUE,
+                     NANOSECONDS.convert(Long.MAX_VALUE / 2, HOURS));
+        assertEquals(Long.MIN_VALUE,
+                     NANOSECONDS.convert(-Long.MAX_VALUE / 4, HOURS));
+        assertEquals(Long.MAX_VALUE,
+                     NANOSECONDS.convert(Long.MAX_VALUE / 2, DAYS));
+        assertEquals(Long.MIN_VALUE,
+                     NANOSECONDS.convert(-Long.MAX_VALUE / 4, DAYS));
+    }
+
+    /**
+     * toNanos saturates positive too-large values to Long.MAX_VALUE
+     * and negative to LONG.MIN_VALUE
+     */
+    public void testToNanosSaturate() {
+        assertEquals(Long.MAX_VALUE,
+                     MILLISECONDS.toNanos(Long.MAX_VALUE / 2));
+        assertEquals(Long.MIN_VALUE,
+                     MILLISECONDS.toNanos(-Long.MAX_VALUE / 3));
+    }
+
+    /**
+     * toString returns name of unit
+     */
+    public void testToString() {
+        assertEquals("SECONDS", SECONDS.toString());
+    }
+
+    /**
+     * name returns name of unit
+     */
+    public void testName() {
+        assertEquals("SECONDS", SECONDS.name());
+    }
+
+    /**
+     * Timed wait without holding lock throws
+     * IllegalMonitorStateException
+     */
+    public void testTimedWait_IllegalMonitorException() {
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                Object o = new Object();
+                TimeUnit tu = MILLISECONDS;
+
+                try {
+                    tu.timedWait(o, LONG_DELAY_MS);
+                    threadShouldThrow();
+                } catch (IllegalMonitorStateException success) {}
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * timedWait throws InterruptedException when interrupted
+     */
+    public void testTimedWait_Interruptible() {
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                Object o = new Object();
+                TimeUnit tu = MILLISECONDS;
+
+                Thread.currentThread().interrupt();
+                try {
+                    synchronized (o) {
+                        tu.timedWait(o, LONG_DELAY_MS);
+                    }
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    synchronized (o) {
+                        tu.timedWait(o, LONG_DELAY_MS);
+                    }
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * timedJoin throws InterruptedException when interrupted
+     */
+    public void testTimedJoin_Interruptible() {
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        final Thread s = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                Thread.sleep(LONG_DELAY_MS);
+            }});
+        final Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                TimeUnit tu = MILLISECONDS;
+                Thread.currentThread().interrupt();
+                try {
+                    tu.timedJoin(s, LONG_DELAY_MS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    tu.timedJoin(s, LONG_DELAY_MS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        s.interrupt();
+        awaitTermination(s);
+    }
+
+    /**
+     * timedSleep throws InterruptedException when interrupted
+     */
+    public void testTimedSleep_Interruptible() {
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                TimeUnit tu = MILLISECONDS;
+                Thread.currentThread().interrupt();
+                try {
+                    tu.sleep(LONG_DELAY_MS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    tu.sleep(LONG_DELAY_MS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * a deserialized serialized unit is the same instance
+     */
+    public void testSerialization() throws Exception {
+        TimeUnit x = MILLISECONDS;
+        assertSame(x, serialClone(x));
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/TreeMapTest.java b/jdk/test/java/util/concurrent/tck/TreeMapTest.java
new file mode 100644
index 00000000000..ebf1dd01b9b
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/TreeMapTest.java
@@ -0,0 +1,1111 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.NavigableSet;
+import java.util.NoSuchElementException;
+import java.util.Random;
+import java.util.Set;
+import java.util.TreeMap;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class TreeMapTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(TreeMapTest.class);
+    }
+
+    /**
+     * Returns a new map from Integers 1-5 to Strings "A"-"E".
+     */
+    private static TreeMap map5() {
+        TreeMap map = new TreeMap();
+        assertTrue(map.isEmpty());
+        map.put(one, "A");
+        map.put(five, "E");
+        map.put(three, "C");
+        map.put(two, "B");
+        map.put(four, "D");
+        assertFalse(map.isEmpty());
+        assertEquals(5, map.size());
+        return map;
+    }
+
+    /**
+     * clear removes all pairs
+     */
+    public void testClear() {
+        TreeMap map = map5();
+        map.clear();
+        assertEquals(0, map.size());
+    }
+
+    /**
+     * copy constructor creates map equal to source map
+     */
+    public void testConstructFromSorted() {
+        TreeMap map = map5();
+        TreeMap map2 = new TreeMap(map);
+        assertEquals(map, map2);
+    }
+
+    /**
+     * Maps with same contents are equal
+     */
+    public void testEquals() {
+        TreeMap map1 = map5();
+        TreeMap map2 = map5();
+        assertEquals(map1, map2);
+        assertEquals(map2, map1);
+        map1.clear();
+        assertFalse(map1.equals(map2));
+        assertFalse(map2.equals(map1));
+    }
+
+    /**
+     * containsKey returns true for contained key
+     */
+    public void testContainsKey() {
+        TreeMap map = map5();
+        assertTrue(map.containsKey(one));
+        assertFalse(map.containsKey(zero));
+    }
+
+    /**
+     * containsValue returns true for held values
+     */
+    public void testContainsValue() {
+        TreeMap map = map5();
+        assertTrue(map.containsValue("A"));
+        assertFalse(map.containsValue("Z"));
+    }
+
+    /**
+     * get returns the correct element at the given key,
+     * or null if not present
+     */
+    public void testGet() {
+        TreeMap map = map5();
+        assertEquals("A", (String)map.get(one));
+        TreeMap empty = new TreeMap();
+        assertNull(empty.get(one));
+    }
+
+    /**
+     * isEmpty is true of empty map and false for non-empty
+     */
+    public void testIsEmpty() {
+        TreeMap empty = new TreeMap();
+        TreeMap map = map5();
+        assertTrue(empty.isEmpty());
+        assertFalse(map.isEmpty());
+    }
+
+    /**
+     * firstKey returns first key
+     */
+    public void testFirstKey() {
+        TreeMap map = map5();
+        assertEquals(one, map.firstKey());
+    }
+
+    /**
+     * lastKey returns last key
+     */
+    public void testLastKey() {
+        TreeMap map = map5();
+        assertEquals(five, map.lastKey());
+    }
+
+    /**
+     * keySet.toArray returns contains all keys
+     */
+    public void testKeySetToArray() {
+        TreeMap map = map5();
+        Set s = map.keySet();
+        Object[] ar = s.toArray();
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        assertEquals(5, ar.length);
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * descendingkeySet.toArray returns contains all keys
+     */
+    public void testDescendingKeySetToArray() {
+        TreeMap map = map5();
+        Set s = map.descendingKeySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * keySet returns a Set containing all the keys
+     */
+    public void testKeySet() {
+        TreeMap map = map5();
+        Set s = map.keySet();
+        assertEquals(5, s.size());
+        assertTrue(s.contains(one));
+        assertTrue(s.contains(two));
+        assertTrue(s.contains(three));
+        assertTrue(s.contains(four));
+        assertTrue(s.contains(five));
+    }
+
+    /**
+     * keySet is ordered
+     */
+    public void testKeySetOrder() {
+        TreeMap map = map5();
+        Set s = map.keySet();
+        Iterator i = s.iterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, one);
+        int count = 1;
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) < 0);
+            last = k;
+            ++count;
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * descending iterator of key set is inverse ordered
+     */
+    public void testKeySetDescendingIteratorOrder() {
+        TreeMap map = map5();
+        NavigableSet s = map.navigableKeySet();
+        Iterator i = s.descendingIterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, five);
+        int count = 1;
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) > 0);
+            last = k;
+            ++count;
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * descendingKeySet is ordered
+     */
+    public void testDescendingKeySetOrder() {
+        TreeMap map = map5();
+        Set s = map.descendingKeySet();
+        Iterator i = s.iterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, five);
+        int count = 1;
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) > 0);
+            last = k;
+            ++count;
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * descending iterator of descendingKeySet is ordered
+     */
+    public void testDescendingKeySetDescendingIteratorOrder() {
+        TreeMap map = map5();
+        NavigableSet s = map.descendingKeySet();
+        Iterator i = s.descendingIterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, one);
+        int count = 1;
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) < 0);
+            last = k;
+            ++count;
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * values collection contains all values
+     */
+    public void testValues() {
+        TreeMap map = map5();
+        Collection s = map.values();
+        assertEquals(5, s.size());
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * entrySet contains all pairs
+     */
+    public void testEntrySet() {
+        TreeMap map = map5();
+        Set s = map.entrySet();
+        assertEquals(5, s.size());
+        Iterator it = s.iterator();
+        while (it.hasNext()) {
+            Map.Entry e = (Map.Entry) it.next();
+            assertTrue(
+                       (e.getKey().equals(one) && e.getValue().equals("A")) ||
+                       (e.getKey().equals(two) && e.getValue().equals("B")) ||
+                       (e.getKey().equals(three) && e.getValue().equals("C")) ||
+                       (e.getKey().equals(four) && e.getValue().equals("D")) ||
+                       (e.getKey().equals(five) && e.getValue().equals("E")));
+        }
+    }
+
+    /**
+     * descendingEntrySet contains all pairs
+     */
+    public void testDescendingEntrySet() {
+        TreeMap map = map5();
+        Set s = map.descendingMap().entrySet();
+        assertEquals(5, s.size());
+        Iterator it = s.iterator();
+        while (it.hasNext()) {
+            Map.Entry e = (Map.Entry) it.next();
+            assertTrue(
+                       (e.getKey().equals(one) && e.getValue().equals("A")) ||
+                       (e.getKey().equals(two) && e.getValue().equals("B")) ||
+                       (e.getKey().equals(three) && e.getValue().equals("C")) ||
+                       (e.getKey().equals(four) && e.getValue().equals("D")) ||
+                       (e.getKey().equals(five) && e.getValue().equals("E")));
+        }
+    }
+
+    /**
+     * entrySet.toArray contains all entries
+     */
+    public void testEntrySetToArray() {
+        TreeMap map = map5();
+        Set s = map.entrySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        for (int i = 0; i < 5; ++i) {
+            assertTrue(map.containsKey(((Map.Entry)(ar[i])).getKey()));
+            assertTrue(map.containsValue(((Map.Entry)(ar[i])).getValue()));
+        }
+    }
+
+    /**
+     * descendingEntrySet.toArray contains all entries
+     */
+    public void testDescendingEntrySetToArray() {
+        TreeMap map = map5();
+        Set s = map.descendingMap().entrySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        for (int i = 0; i < 5; ++i) {
+            assertTrue(map.containsKey(((Map.Entry)(ar[i])).getKey()));
+            assertTrue(map.containsValue(((Map.Entry)(ar[i])).getValue()));
+        }
+    }
+
+    /**
+     * putAll adds all key-value pairs from the given map
+     */
+    public void testPutAll() {
+        TreeMap empty = new TreeMap();
+        TreeMap map = map5();
+        empty.putAll(map);
+        assertEquals(5, empty.size());
+        assertTrue(empty.containsKey(one));
+        assertTrue(empty.containsKey(two));
+        assertTrue(empty.containsKey(three));
+        assertTrue(empty.containsKey(four));
+        assertTrue(empty.containsKey(five));
+    }
+
+    /**
+     * remove removes the correct key-value pair from the map
+     */
+    public void testRemove() {
+        TreeMap map = map5();
+        map.remove(five);
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(five));
+    }
+
+    /**
+     * lowerEntry returns preceding entry.
+     */
+    public void testLowerEntry() {
+        TreeMap map = map5();
+        Map.Entry e1 = map.lowerEntry(three);
+        assertEquals(two, e1.getKey());
+
+        Map.Entry e2 = map.lowerEntry(six);
+        assertEquals(five, e2.getKey());
+
+        Map.Entry e3 = map.lowerEntry(one);
+        assertNull(e3);
+
+        Map.Entry e4 = map.lowerEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higherEntry returns next entry.
+     */
+    public void testHigherEntry() {
+        TreeMap map = map5();
+        Map.Entry e1 = map.higherEntry(three);
+        assertEquals(four, e1.getKey());
+
+        Map.Entry e2 = map.higherEntry(zero);
+        assertEquals(one, e2.getKey());
+
+        Map.Entry e3 = map.higherEntry(five);
+        assertNull(e3);
+
+        Map.Entry e4 = map.higherEntry(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floorEntry returns preceding entry.
+     */
+    public void testFloorEntry() {
+        TreeMap map = map5();
+        Map.Entry e1 = map.floorEntry(three);
+        assertEquals(three, e1.getKey());
+
+        Map.Entry e2 = map.floorEntry(six);
+        assertEquals(five, e2.getKey());
+
+        Map.Entry e3 = map.floorEntry(one);
+        assertEquals(one, e3.getKey());
+
+        Map.Entry e4 = map.floorEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceilingEntry returns next entry.
+     */
+    public void testCeilingEntry() {
+        TreeMap map = map5();
+        Map.Entry e1 = map.ceilingEntry(three);
+        assertEquals(three, e1.getKey());
+
+        Map.Entry e2 = map.ceilingEntry(zero);
+        assertEquals(one, e2.getKey());
+
+        Map.Entry e3 = map.ceilingEntry(five);
+        assertEquals(five, e3.getKey());
+
+        Map.Entry e4 = map.ceilingEntry(six);
+        assertNull(e4);
+    }
+
+    /**
+     * lowerKey returns preceding element
+     */
+    public void testLowerKey() {
+        TreeMap q = map5();
+        Object e1 = q.lowerKey(three);
+        assertEquals(two, e1);
+
+        Object e2 = q.lowerKey(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.lowerKey(one);
+        assertNull(e3);
+
+        Object e4 = q.lowerKey(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higherKey returns next element
+     */
+    public void testHigherKey() {
+        TreeMap q = map5();
+        Object e1 = q.higherKey(three);
+        assertEquals(four, e1);
+
+        Object e2 = q.higherKey(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.higherKey(five);
+        assertNull(e3);
+
+        Object e4 = q.higherKey(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floorKey returns preceding element
+     */
+    public void testFloorKey() {
+        TreeMap q = map5();
+        Object e1 = q.floorKey(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.floorKey(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.floorKey(one);
+        assertEquals(one, e3);
+
+        Object e4 = q.floorKey(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceilingKey returns next element
+     */
+    public void testCeilingKey() {
+        TreeMap q = map5();
+        Object e1 = q.ceilingKey(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.ceilingKey(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.ceilingKey(five);
+        assertEquals(five, e3);
+
+        Object e4 = q.ceilingKey(six);
+        assertNull(e4);
+    }
+
+    /**
+     * pollFirstEntry returns entries in order
+     */
+    public void testPollFirstEntry() {
+        TreeMap map = map5();
+        Map.Entry e = map.pollFirstEntry();
+        assertEquals(one, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(two, e.getKey());
+        map.put(one, "A");
+        e = map.pollFirstEntry();
+        assertEquals(one, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(three, e.getKey());
+        map.remove(four);
+        e = map.pollFirstEntry();
+        assertEquals(five, e.getKey());
+        try {
+            e.setValue("A");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollFirstEntry();
+        assertNull(e);
+    }
+
+    /**
+     * pollLastEntry returns entries in order
+     */
+    public void testPollLastEntry() {
+        TreeMap map = map5();
+        Map.Entry e = map.pollLastEntry();
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(four, e.getKey());
+        map.put(five, "E");
+        e = map.pollLastEntry();
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(three, e.getKey());
+        map.remove(two);
+        e = map.pollLastEntry();
+        assertEquals(one, e.getKey());
+        try {
+            e.setValue("E");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollLastEntry();
+        assertNull(e);
+    }
+
+    /**
+     * size returns the correct values
+     */
+    public void testSize() {
+        TreeMap map = map5();
+        TreeMap empty = new TreeMap();
+        assertEquals(0, empty.size());
+        assertEquals(5, map.size());
+    }
+
+    /**
+     * toString contains toString of elements
+     */
+    public void testToString() {
+        TreeMap map = map5();
+        String s = map.toString();
+        for (int i = 1; i <= 5; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    // Exception tests
+
+    /**
+     * get(null) of nonempty map throws NPE
+     */
+    public void testGet_NullPointerException() {
+        TreeMap c = map5();
+        try {
+            c.get(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsKey(null) of nonempty map throws NPE
+     */
+    public void testContainsKey_NullPointerException() {
+        TreeMap c = map5();
+        try {
+            c.containsKey(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null) throws NPE for nonempty map
+     */
+    public void testRemove1_NullPointerException() {
+        TreeMap c = new TreeMap();
+        c.put("sadsdf", "asdads");
+        try {
+            c.remove(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * A deserialized map equals original
+     */
+    public void testSerialization() throws Exception {
+        NavigableMap x = map5();
+        NavigableMap y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    /**
+     * subMap returns map with keys in requested range
+     */
+    public void testSubMapContents() {
+        TreeMap map = map5();
+        NavigableMap sm = map.subMap(two, true, four, false);
+        assertEquals(two, sm.firstKey());
+        assertEquals(three, sm.lastKey());
+        assertEquals(2, sm.size());
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        Iterator r = sm.descendingKeySet().iterator();
+        k = (Integer)(r.next());
+        assertEquals(three, k);
+        k = (Integer)(r.next());
+        assertEquals(two, k);
+        assertFalse(r.hasNext());
+
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(two));
+        assertEquals(4, map.size());
+        assertEquals(1, sm.size());
+        assertEquals(three, sm.firstKey());
+        assertEquals(three, sm.lastKey());
+        assertEquals("C", sm.remove(three));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, map.size());
+    }
+
+    public void testSubMapContents2() {
+        TreeMap map = map5();
+        NavigableMap sm = map.subMap(two, true, three, false);
+        assertEquals(1, sm.size());
+        assertEquals(two, sm.firstKey());
+        assertEquals(two, sm.lastKey());
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertFalse(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        assertFalse(i.hasNext());
+        Iterator r = sm.descendingKeySet().iterator();
+        k = (Integer)(r.next());
+        assertEquals(two, k);
+        assertFalse(r.hasNext());
+
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(two));
+        assertEquals(4, map.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertSame(sm.remove(three), null);
+        assertEquals(4, map.size());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testHeadMapContents() {
+        TreeMap map = map5();
+        NavigableMap sm = map.headMap(four, false);
+        assertTrue(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(one, k);
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, map.size());
+        assertEquals(four, map.firstKey());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testTailMapContents() {
+        TreeMap map = map5();
+        NavigableMap sm = map.tailMap(two, true);
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertTrue(sm.containsKey(four));
+        assertTrue(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        k = (Integer)(i.next());
+        assertEquals(four, k);
+        k = (Integer)(i.next());
+        assertEquals(five, k);
+        assertFalse(i.hasNext());
+        Iterator r = sm.descendingKeySet().iterator();
+        k = (Integer)(r.next());
+        assertEquals(five, k);
+        k = (Integer)(r.next());
+        assertEquals(four, k);
+        k = (Integer)(r.next());
+        assertEquals(three, k);
+        k = (Integer)(r.next());
+        assertEquals(two, k);
+        assertFalse(r.hasNext());
+
+        Iterator ei = sm.entrySet().iterator();
+        Map.Entry e;
+        e = (Map.Entry)(ei.next());
+        assertEquals(two, e.getKey());
+        assertEquals("B", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(three, e.getKey());
+        assertEquals("C", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(four, e.getKey());
+        assertEquals("D", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        assertFalse(i.hasNext());
+
+        NavigableMap ssm = sm.tailMap(four, true);
+        assertEquals(four, ssm.firstKey());
+        assertEquals(five, ssm.lastKey());
+        assertEquals("D", ssm.remove(four));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, map.size());
+    }
+
+    Random rnd = new Random(666);
+    BitSet bs;
+
+    /**
+     * Submaps of submaps subdivide correctly
+     */
+    public void testRecursiveSubMaps() throws Exception {
+        int mapSize = expensiveTests ? 1000 : 100;
+        Class cl = TreeMap.class;
+        NavigableMap<Integer, Integer> map = newMap(cl);
+        bs = new BitSet(mapSize);
+
+        populate(map, mapSize);
+        check(map,                 0, mapSize - 1, true);
+        check(map.descendingMap(), 0, mapSize - 1, false);
+
+        mutateMap(map, 0, mapSize - 1);
+        check(map,                 0, mapSize - 1, true);
+        check(map.descendingMap(), 0, mapSize - 1, false);
+
+        bashSubMap(map.subMap(0, true, mapSize, false),
+                   0, mapSize - 1, true);
+    }
+
+    static NavigableMap<Integer, Integer> newMap(Class cl) throws Exception {
+        NavigableMap<Integer, Integer> result
+            = (NavigableMap<Integer, Integer>) cl.newInstance();
+        assertEquals(0, result.size());
+        assertFalse(result.keySet().iterator().hasNext());
+        return result;
+    }
+
+    void populate(NavigableMap<Integer, Integer> map, int limit) {
+        for (int i = 0, n = 2 * limit / 3; i < n; i++) {
+            int key = rnd.nextInt(limit);
+            put(map, key);
+        }
+    }
+
+    void mutateMap(NavigableMap<Integer, Integer> map, int min, int max) {
+        int size = map.size();
+        int rangeSize = max - min + 1;
+
+        // Remove a bunch of entries directly
+        for (int i = 0, n = rangeSize / 2; i < n; i++) {
+            remove(map, min - 5 + rnd.nextInt(rangeSize + 10));
+        }
+
+        // Remove a bunch of entries with iterator
+        for (Iterator<Integer> it = map.keySet().iterator(); it.hasNext(); ) {
+            if (rnd.nextBoolean()) {
+                bs.clear(it.next());
+                it.remove();
+            }
+        }
+
+        // Add entries till we're back to original size
+        while (map.size() < size) {
+            int key = min + rnd.nextInt(rangeSize);
+            assertTrue(key >= min && key <= max);
+            put(map, key);
+        }
+    }
+
+    void mutateSubMap(NavigableMap<Integer, Integer> map, int min, int max) {
+        int size = map.size();
+        int rangeSize = max - min + 1;
+
+        // Remove a bunch of entries directly
+        for (int i = 0, n = rangeSize / 2; i < n; i++) {
+            remove(map, min - 5 + rnd.nextInt(rangeSize + 10));
+        }
+
+        // Remove a bunch of entries with iterator
+        for (Iterator<Integer> it = map.keySet().iterator(); it.hasNext(); ) {
+            if (rnd.nextBoolean()) {
+                bs.clear(it.next());
+                it.remove();
+            }
+        }
+
+        // Add entries till we're back to original size
+        while (map.size() < size) {
+            int key = min - 5 + rnd.nextInt(rangeSize + 10);
+            if (key >= min && key <= max) {
+                put(map, key);
+            } else {
+                try {
+                    map.put(key, 2 * key);
+                    shouldThrow();
+                } catch (IllegalArgumentException success) {}
+            }
+        }
+    }
+
+    void put(NavigableMap<Integer, Integer> map, int key) {
+        if (map.put(key, 2 * key) == null)
+            bs.set(key);
+    }
+
+    void remove(NavigableMap<Integer, Integer> map, int key) {
+        if (map.remove(key) != null)
+            bs.clear(key);
+    }
+
+    void bashSubMap(NavigableMap<Integer, Integer> map,
+                    int min, int max, boolean ascending) {
+        check(map, min, max, ascending);
+        check(map.descendingMap(), min, max, !ascending);
+
+        mutateSubMap(map, min, max);
+        check(map, min, max, ascending);
+        check(map.descendingMap(), min, max, !ascending);
+
+        // Recurse
+        if (max - min < 2)
+            return;
+        int midPoint = (min + max) / 2;
+
+        // headMap - pick direction and endpoint inclusion randomly
+        boolean incl = rnd.nextBoolean();
+        NavigableMap<Integer,Integer> hm = map.headMap(midPoint, incl);
+        if (ascending) {
+            if (rnd.nextBoolean())
+                bashSubMap(hm, min, midPoint - (incl ? 0 : 1), true);
+            else
+                bashSubMap(hm.descendingMap(), min, midPoint - (incl ? 0 : 1),
+                           false);
+        } else {
+            if (rnd.nextBoolean())
+                bashSubMap(hm, midPoint + (incl ? 0 : 1), max, false);
+            else
+                bashSubMap(hm.descendingMap(), midPoint + (incl ? 0 : 1), max,
+                           true);
+        }
+
+        // tailMap - pick direction and endpoint inclusion randomly
+        incl = rnd.nextBoolean();
+        NavigableMap<Integer,Integer> tm = map.tailMap(midPoint,incl);
+        if (ascending) {
+            if (rnd.nextBoolean())
+                bashSubMap(tm, midPoint + (incl ? 0 : 1), max, true);
+            else
+                bashSubMap(tm.descendingMap(), midPoint + (incl ? 0 : 1), max,
+                           false);
+        } else {
+            if (rnd.nextBoolean()) {
+                bashSubMap(tm, min, midPoint - (incl ? 0 : 1), false);
+            } else {
+                bashSubMap(tm.descendingMap(), min, midPoint - (incl ? 0 : 1),
+                           true);
+            }
+        }
+
+        // subMap - pick direction and endpoint inclusion randomly
+        int rangeSize = max - min + 1;
+        int[] endpoints = new int[2];
+        endpoints[0] = min + rnd.nextInt(rangeSize);
+        endpoints[1] = min + rnd.nextInt(rangeSize);
+        Arrays.sort(endpoints);
+        boolean lowIncl = rnd.nextBoolean();
+        boolean highIncl = rnd.nextBoolean();
+        if (ascending) {
+            NavigableMap<Integer,Integer> sm = map.subMap(
+                endpoints[0], lowIncl, endpoints[1], highIncl);
+            if (rnd.nextBoolean())
+                bashSubMap(sm, endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), true);
+            else
+                bashSubMap(sm.descendingMap(), endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), false);
+        } else {
+            NavigableMap<Integer,Integer> sm = map.subMap(
+                endpoints[1], highIncl, endpoints[0], lowIncl);
+            if (rnd.nextBoolean())
+                bashSubMap(sm, endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), false);
+            else
+                bashSubMap(sm.descendingMap(), endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), true);
+        }
+    }
+
+    /**
+     * min and max are both inclusive.  If max < min, interval is empty.
+     */
+    void check(NavigableMap<Integer, Integer> map,
+                      final int min, final int max, final boolean ascending) {
+        class ReferenceSet {
+            int lower(int key) {
+                return ascending ? lowerAscending(key) : higherAscending(key);
+            }
+            int floor(int key) {
+                return ascending ? floorAscending(key) : ceilingAscending(key);
+            }
+            int ceiling(int key) {
+                return ascending ? ceilingAscending(key) : floorAscending(key);
+            }
+            int higher(int key) {
+                return ascending ? higherAscending(key) : lowerAscending(key);
+            }
+            int first() {
+                return ascending ? firstAscending() : lastAscending();
+            }
+            int last() {
+                return ascending ? lastAscending() : firstAscending();
+            }
+            int lowerAscending(int key) {
+                return floorAscending(key - 1);
+            }
+            int floorAscending(int key) {
+                if (key < min)
+                    return -1;
+                else if (key > max)
+                    key = max;
+
+                // BitSet should support this! Test would run much faster
+                while (key >= min) {
+                    if (bs.get(key))
+                        return key;
+                    key--;
+                }
+                return -1;
+            }
+            int ceilingAscending(int key) {
+                if (key < min)
+                    key = min;
+                else if (key > max)
+                    return -1;
+                int result = bs.nextSetBit(key);
+                return result > max ? -1 : result;
+            }
+            int higherAscending(int key) {
+                return ceilingAscending(key + 1);
+            }
+            private int firstAscending() {
+                int result = ceilingAscending(min);
+                return result > max ? -1 : result;
+            }
+            private int lastAscending() {
+                int result = floorAscending(max);
+                return result < min ? -1 : result;
+            }
+        }
+        ReferenceSet rs = new ReferenceSet();
+
+        // Test contents using containsKey
+        int size = 0;
+        for (int i = min; i <= max; i++) {
+            boolean bsContainsI = bs.get(i);
+            assertEquals(bsContainsI, map.containsKey(i));
+            if (bsContainsI)
+                size++;
+        }
+        assertEquals(size, map.size());
+
+        // Test contents using contains keySet iterator
+        int size2 = 0;
+        int previousKey = -1;
+        for (int key : map.keySet()) {
+            assertTrue(bs.get(key));
+            size2++;
+            assertTrue(previousKey < 0 ||
+                (ascending ? key - previousKey > 0 : key - previousKey < 0));
+            previousKey = key;
+        }
+        assertEquals(size2, size);
+
+        // Test navigation ops
+        for (int key = min - 1; key <= max + 1; key++) {
+            assertEq(map.lowerKey(key), rs.lower(key));
+            assertEq(map.floorKey(key), rs.floor(key));
+            assertEq(map.higherKey(key), rs.higher(key));
+            assertEq(map.ceilingKey(key), rs.ceiling(key));
+        }
+
+        // Test extrema
+        if (map.size() != 0) {
+            assertEq(map.firstKey(), rs.first());
+            assertEq(map.lastKey(), rs.last());
+        } else {
+            assertEq(rs.first(), -1);
+            assertEq(rs.last(),  -1);
+            try {
+                map.firstKey();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+            try {
+                map.lastKey();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+        }
+    }
+
+    static void assertEq(Integer i, int j) {
+        if (i == null)
+            assertEquals(j, -1);
+        else
+            assertEquals((int) i, j);
+    }
+
+    static boolean eq(Integer i, int j) {
+        return i == null ? j == -1 : i == j;
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/TreeSetTest.java b/jdk/test/java/util/concurrent/tck/TreeSetTest.java
new file mode 100644
index 00000000000..eb7b6efd9d0
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/TreeSetTest.java
@@ -0,0 +1,1008 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.NavigableSet;
+import java.util.NoSuchElementException;
+import java.util.Random;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class TreeSetTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(TreeSetTest.class);
+    }
+
+    static class MyReverseComparator implements Comparator {
+        public int compare(Object x, Object y) {
+            return ((Comparable)y).compareTo(x);
+        }
+    }
+
+    /**
+     * The number of elements to place in collections, arrays, etc.
+     */
+    static final int SIZE = 20;
+
+    /**
+     * Returns a new set of given size containing consecutive
+     * Integers 0 ... n.
+     */
+    private TreeSet<Integer> populatedSet(int n) {
+        TreeSet<Integer> q = new TreeSet<Integer>();
+        assertTrue(q.isEmpty());
+        for (int i = n - 1; i >= 0; i -= 2)
+            assertTrue(q.add(new Integer(i)));
+        for (int i = (n & 1); i < n; i += 2)
+            assertTrue(q.add(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(n, q.size());
+        return q;
+    }
+
+    /**
+     * Returns a new set of first 5 ints.
+     */
+    private TreeSet set5() {
+        TreeSet q = new TreeSet();
+        assertTrue(q.isEmpty());
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        q.add(four);
+        q.add(five);
+        assertEquals(5, q.size());
+        return q;
+    }
+
+    /**
+     * A new set has unbounded capacity
+     */
+    public void testConstructor1() {
+        assertEquals(0, new TreeSet().size());
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            new TreeSet((Collection)null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NPE
+     */
+    public void testConstructor4() {
+        try {
+            new TreeSet(Arrays.asList(new Integer[SIZE]));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws NPE
+     */
+    public void testConstructor5() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            new TreeSet(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Set contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        TreeSet q = new TreeSet(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.pollFirst());
+    }
+
+    /**
+     * The comparator used in constructor is used
+     */
+    public void testConstructor7() {
+        MyReverseComparator cmp = new MyReverseComparator();
+        TreeSet q = new TreeSet(cmp);
+        assertEquals(cmp, q.comparator());
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        q.addAll(Arrays.asList(ints));
+        for (int i = SIZE - 1; i >= 0; --i)
+            assertEquals(ints[i], q.pollFirst());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        TreeSet q = new TreeSet();
+        assertTrue(q.isEmpty());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.add(new Integer(2));
+        q.pollFirst();
+        q.pollFirst();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testSize() {
+        TreeSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i, q.size());
+            q.pollFirst();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * add(null) throws NPE if nonempty
+     */
+    public void testAddNull() {
+        TreeSet q = populatedSet(SIZE);
+        try {
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Add of comparable element succeeds
+     */
+    public void testAdd() {
+        TreeSet q = new TreeSet();
+        assertTrue(q.add(zero));
+        assertTrue(q.add(one));
+    }
+
+    /**
+     * Add of duplicate element fails
+     */
+    public void testAddDup() {
+        TreeSet q = new TreeSet();
+        assertTrue(q.add(zero));
+        assertFalse(q.add(zero));
+    }
+
+    /**
+     * Add of non-Comparable throws CCE
+     */
+    public void testAddNonComparable() {
+        TreeSet q = new TreeSet();
+        try {
+            q.add(new Object());
+            q.add(new Object());
+            shouldThrow();
+        } catch (ClassCastException success) {}
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        TreeSet q = new TreeSet();
+        try {
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testAddAll2() {
+        TreeSet q = new TreeSet();
+        Integer[] ints = new Integer[SIZE];
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        TreeSet q = new TreeSet();
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Set contains all elements of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(SIZE - 1 - i);
+        TreeSet q = new TreeSet();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(new Integer(i), q.pollFirst());
+    }
+
+    /**
+     * pollFirst succeeds unless empty
+     */
+    public void testPollFirst() {
+        TreeSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * pollLast succeeds unless empty
+     */
+    public void testPollLast() {
+        TreeSet q = populatedSet(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.pollLast());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testRemoveElement() {
+        TreeSet q = populatedSet(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertTrue(q.contains(i - 1));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertFalse(q.remove(i + 1));
+            assertFalse(q.contains(i + 1));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        TreeSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.pollFirst();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        TreeSet q = populatedSet(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        TreeSet q = populatedSet(SIZE);
+        TreeSet p = new TreeSet();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        TreeSet q = populatedSet(SIZE);
+        TreeSet p = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.pollFirst();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            TreeSet q = populatedSet(SIZE);
+            TreeSet p = populatedSet(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer x = (Integer)(p.pollFirst());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    /**
+     * lower returns preceding element
+     */
+    public void testLower() {
+        TreeSet q = set5();
+        Object e1 = q.lower(three);
+        assertEquals(two, e1);
+
+        Object e2 = q.lower(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.lower(one);
+        assertNull(e3);
+
+        Object e4 = q.lower(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higher returns next element
+     */
+    public void testHigher() {
+        TreeSet q = set5();
+        Object e1 = q.higher(three);
+        assertEquals(four, e1);
+
+        Object e2 = q.higher(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.higher(five);
+        assertNull(e3);
+
+        Object e4 = q.higher(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floor returns preceding element
+     */
+    public void testFloor() {
+        TreeSet q = set5();
+        Object e1 = q.floor(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.floor(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.floor(one);
+        assertEquals(one, e3);
+
+        Object e4 = q.floor(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceiling returns next element
+     */
+    public void testCeiling() {
+        TreeSet q = set5();
+        Object e1 = q.ceiling(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.ceiling(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.ceiling(five);
+        assertEquals(five, e3);
+
+        Object e4 = q.ceiling(six);
+        assertNull(e4);
+    }
+
+    /**
+     * toArray contains all elements in sorted order
+     */
+    public void testToArray() {
+        TreeSet q = populatedSet(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.pollFirst());
+    }
+
+    /**
+     * toArray(a) contains all elements in sorted order
+     */
+    public void testToArray2() {
+        TreeSet<Integer> q = populatedSet(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.pollFirst());
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() {
+        TreeSet q = populatedSet(SIZE);
+        Iterator it = q.iterator();
+        int i;
+        for (i = 0; it.hasNext(); i++)
+            assertTrue(q.contains(it.next()));
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty set has no elements
+     */
+    public void testEmptyIterator() {
+        assertIteratorExhausted(new TreeSet().iterator());
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final TreeSet q = new TreeSet();
+        q.add(new Integer(2));
+        q.add(new Integer(1));
+        q.add(new Integer(3));
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertEquals(it.next(), new Integer(2));
+        assertEquals(it.next(), new Integer(3));
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        TreeSet q = populatedSet(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized set has same elements
+     */
+    public void testSerialization() throws Exception {
+        NavigableSet x = populatedSet(SIZE);
+        NavigableSet y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x, y);
+        assertEquals(y, x);
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.pollFirst(), y.pollFirst());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * subSet returns set with keys in requested range
+     */
+    public void testSubSetContents() {
+        TreeSet set = set5();
+        SortedSet sm = set.subSet(two, four);
+        assertEquals(two, sm.first());
+        assertEquals(three, sm.last());
+        assertEquals(2, sm.size());
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(two));
+        assertEquals(4, set.size());
+        assertEquals(1, sm.size());
+        assertEquals(three, sm.first());
+        assertEquals(three, sm.last());
+        assertTrue(sm.remove(three));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, set.size());
+    }
+
+    public void testSubSetContents2() {
+        TreeSet set = set5();
+        SortedSet sm = set.subSet(two, three);
+        assertEquals(1, sm.size());
+        assertEquals(two, sm.first());
+        assertEquals(two, sm.last());
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertFalse(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(two));
+        assertEquals(4, set.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertFalse(sm.remove(three));
+        assertEquals(4, set.size());
+    }
+
+    /**
+     * headSet returns set with keys in requested range
+     */
+    public void testHeadSetContents() {
+        TreeSet set = set5();
+        SortedSet sm = set.headSet(four);
+        assertTrue(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(one, k);
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, set.size());
+        assertEquals(four, set.first());
+    }
+
+    /**
+     * tailSet returns set with keys in requested range
+     */
+    public void testTailSetContents() {
+        TreeSet set = set5();
+        SortedSet sm = set.tailSet(two);
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertTrue(sm.contains(four));
+        assertTrue(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        k = (Integer)(i.next());
+        assertEquals(four, k);
+        k = (Integer)(i.next());
+        assertEquals(five, k);
+        assertFalse(i.hasNext());
+
+        SortedSet ssm = sm.tailSet(four);
+        assertEquals(four, ssm.first());
+        assertEquals(five, ssm.last());
+        assertTrue(ssm.remove(four));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, set.size());
+    }
+
+    Random rnd = new Random(666);
+    BitSet bs;
+
+    /**
+     * Subsets of subsets subdivide correctly
+     */
+    public void testRecursiveSubSets() throws Exception {
+        int setSize = expensiveTests ? 1000 : 100;
+        Class cl = TreeSet.class;
+
+        NavigableSet<Integer> set = newSet(cl);
+        bs = new BitSet(setSize);
+
+        populate(set, setSize);
+        check(set,                 0, setSize - 1, true);
+        check(set.descendingSet(), 0, setSize - 1, false);
+
+        mutateSet(set, 0, setSize - 1);
+        check(set,                 0, setSize - 1, true);
+        check(set.descendingSet(), 0, setSize - 1, false);
+
+        bashSubSet(set.subSet(0, true, setSize, false),
+                   0, setSize - 1, true);
+    }
+
+    /**
+     * addAll is idempotent
+     */
+    public void testAddAll_idempotent() throws Exception {
+        Set x = populatedSet(SIZE);
+        Set y = new TreeSet(x);
+        y.addAll(x);
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    static NavigableSet<Integer> newSet(Class cl) throws Exception {
+        NavigableSet<Integer> result = (NavigableSet<Integer>) cl.newInstance();
+        assertEquals(0, result.size());
+        assertFalse(result.iterator().hasNext());
+        return result;
+    }
+
+    void populate(NavigableSet<Integer> set, int limit) {
+        for (int i = 0, n = 2 * limit / 3; i < n; i++) {
+            int element = rnd.nextInt(limit);
+            put(set, element);
+        }
+    }
+
+    void mutateSet(NavigableSet<Integer> set, int min, int max) {
+        int size = set.size();
+        int rangeSize = max - min + 1;
+
+        // Remove a bunch of entries directly
+        for (int i = 0, n = rangeSize / 2; i < n; i++) {
+            remove(set, min - 5 + rnd.nextInt(rangeSize + 10));
+        }
+
+        // Remove a bunch of entries with iterator
+        for (Iterator<Integer> it = set.iterator(); it.hasNext(); ) {
+            if (rnd.nextBoolean()) {
+                bs.clear(it.next());
+                it.remove();
+            }
+        }
+
+        // Add entries till we're back to original size
+        while (set.size() < size) {
+            int element = min + rnd.nextInt(rangeSize);
+            assertTrue(element >= min && element <= max);
+            put(set, element);
+        }
+    }
+
+    void mutateSubSet(NavigableSet<Integer> set, int min, int max) {
+        int size = set.size();
+        int rangeSize = max - min + 1;
+
+        // Remove a bunch of entries directly
+        for (int i = 0, n = rangeSize / 2; i < n; i++) {
+            remove(set, min - 5 + rnd.nextInt(rangeSize + 10));
+        }
+
+        // Remove a bunch of entries with iterator
+        for (Iterator<Integer> it = set.iterator(); it.hasNext(); ) {
+            if (rnd.nextBoolean()) {
+                bs.clear(it.next());
+                it.remove();
+            }
+        }
+
+        // Add entries till we're back to original size
+        while (set.size() < size) {
+            int element = min - 5 + rnd.nextInt(rangeSize + 10);
+            if (element >= min && element <= max) {
+                put(set, element);
+            } else {
+                try {
+                    set.add(element);
+                    shouldThrow();
+                } catch (IllegalArgumentException success) {}
+            }
+        }
+    }
+
+    void put(NavigableSet<Integer> set, int element) {
+        if (set.add(element))
+            bs.set(element);
+    }
+
+    void remove(NavigableSet<Integer> set, int element) {
+        if (set.remove(element))
+            bs.clear(element);
+    }
+
+    void bashSubSet(NavigableSet<Integer> set,
+                    int min, int max, boolean ascending) {
+        check(set, min, max, ascending);
+        check(set.descendingSet(), min, max, !ascending);
+
+        mutateSubSet(set, min, max);
+        check(set, min, max, ascending);
+        check(set.descendingSet(), min, max, !ascending);
+
+        // Recurse
+        if (max - min < 2)
+            return;
+        int midPoint = (min + max) / 2;
+
+        // headSet - pick direction and endpoint inclusion randomly
+        boolean incl = rnd.nextBoolean();
+        NavigableSet<Integer> hm = set.headSet(midPoint, incl);
+        if (ascending) {
+            if (rnd.nextBoolean())
+                bashSubSet(hm, min, midPoint - (incl ? 0 : 1), true);
+            else
+                bashSubSet(hm.descendingSet(), min, midPoint - (incl ? 0 : 1),
+                           false);
+        } else {
+            if (rnd.nextBoolean())
+                bashSubSet(hm, midPoint + (incl ? 0 : 1), max, false);
+            else
+                bashSubSet(hm.descendingSet(), midPoint + (incl ? 0 : 1), max,
+                           true);
+        }
+
+        // tailSet - pick direction and endpoint inclusion randomly
+        incl = rnd.nextBoolean();
+        NavigableSet<Integer> tm = set.tailSet(midPoint,incl);
+        if (ascending) {
+            if (rnd.nextBoolean())
+                bashSubSet(tm, midPoint + (incl ? 0 : 1), max, true);
+            else
+                bashSubSet(tm.descendingSet(), midPoint + (incl ? 0 : 1), max,
+                           false);
+        } else {
+            if (rnd.nextBoolean()) {
+                bashSubSet(tm, min, midPoint - (incl ? 0 : 1), false);
+            } else {
+                bashSubSet(tm.descendingSet(), min, midPoint - (incl ? 0 : 1),
+                           true);
+            }
+        }
+
+        // subSet - pick direction and endpoint inclusion randomly
+        int rangeSize = max - min + 1;
+        int[] endpoints = new int[2];
+        endpoints[0] = min + rnd.nextInt(rangeSize);
+        endpoints[1] = min + rnd.nextInt(rangeSize);
+        Arrays.sort(endpoints);
+        boolean lowIncl = rnd.nextBoolean();
+        boolean highIncl = rnd.nextBoolean();
+        if (ascending) {
+            NavigableSet<Integer> sm = set.subSet(
+                endpoints[0], lowIncl, endpoints[1], highIncl);
+            if (rnd.nextBoolean())
+                bashSubSet(sm, endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), true);
+            else
+                bashSubSet(sm.descendingSet(), endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), false);
+        } else {
+            NavigableSet<Integer> sm = set.subSet(
+                endpoints[1], highIncl, endpoints[0], lowIncl);
+            if (rnd.nextBoolean())
+                bashSubSet(sm, endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), false);
+            else
+                bashSubSet(sm.descendingSet(), endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), true);
+        }
+    }
+
+    /**
+     * min and max are both inclusive.  If max < min, interval is empty.
+     */
+    void check(NavigableSet<Integer> set,
+                      final int min, final int max, final boolean ascending) {
+        class ReferenceSet {
+            int lower(int element) {
+                return ascending ?
+                    lowerAscending(element) : higherAscending(element);
+            }
+            int floor(int element) {
+                return ascending ?
+                    floorAscending(element) : ceilingAscending(element);
+            }
+            int ceiling(int element) {
+                return ascending ?
+                    ceilingAscending(element) : floorAscending(element);
+            }
+            int higher(int element) {
+                return ascending ?
+                    higherAscending(element) : lowerAscending(element);
+            }
+            int first() {
+                return ascending ? firstAscending() : lastAscending();
+            }
+            int last() {
+                return ascending ? lastAscending() : firstAscending();
+            }
+            int lowerAscending(int element) {
+                return floorAscending(element - 1);
+            }
+            int floorAscending(int element) {
+                if (element < min)
+                    return -1;
+                else if (element > max)
+                    element = max;
+
+                // BitSet should support this! Test would run much faster
+                while (element >= min) {
+                    if (bs.get(element))
+                        return element;
+                    element--;
+                }
+                return -1;
+            }
+            int ceilingAscending(int element) {
+                if (element < min)
+                    element = min;
+                else if (element > max)
+                    return -1;
+                int result = bs.nextSetBit(element);
+                return (result > max) ? -1 : result;
+            }
+            int higherAscending(int element) {
+                return ceilingAscending(element + 1);
+            }
+            private int firstAscending() {
+                int result = ceilingAscending(min);
+                return (result > max) ? -1 : result;
+            }
+            private int lastAscending() {
+                int result = floorAscending(max);
+                return (result < min) ? -1 : result;
+            }
+        }
+        ReferenceSet rs = new ReferenceSet();
+
+        // Test contents using containsElement
+        int size = 0;
+        for (int i = min; i <= max; i++) {
+            boolean bsContainsI = bs.get(i);
+            assertEquals(bsContainsI, set.contains(i));
+            if (bsContainsI)
+                size++;
+        }
+        assertEquals(size, set.size());
+
+        // Test contents using contains elementSet iterator
+        int size2 = 0;
+        int previousElement = -1;
+        for (int element : set) {
+            assertTrue(bs.get(element));
+            size2++;
+            assertTrue(previousElement < 0 || (ascending ?
+                element - previousElement > 0 : element - previousElement < 0));
+            previousElement = element;
+        }
+        assertEquals(size2, size);
+
+        // Test navigation ops
+        for (int element = min - 1; element <= max + 1; element++) {
+            assertEq(set.lower(element), rs.lower(element));
+            assertEq(set.floor(element), rs.floor(element));
+            assertEq(set.higher(element), rs.higher(element));
+            assertEq(set.ceiling(element), rs.ceiling(element));
+        }
+
+        // Test extrema
+        if (set.size() != 0) {
+            assertEq(set.first(), rs.first());
+            assertEq(set.last(), rs.last());
+        } else {
+            assertEq(rs.first(), -1);
+            assertEq(rs.last(),  -1);
+            try {
+                set.first();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+            try {
+                set.last();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+        }
+    }
+
+    static void assertEq(Integer i, int j) {
+        if (i == null)
+            assertEquals(j, -1);
+        else
+            assertEquals((int) i, j);
+    }
+
+    static boolean eq(Integer i, int j) {
+        return (i == null) ? j == -1 : i == j;
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/TreeSubMapTest.java b/jdk/test/java/util/concurrent/tck/TreeSubMapTest.java
new file mode 100644
index 00000000000..1b5fa424b1c
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/TreeSubMapTest.java
@@ -0,0 +1,1138 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class TreeSubMapTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(TreeSubMapTest.class);
+    }
+
+    /**
+     * Returns a new map from Integers 1-5 to Strings "A"-"E".
+     */
+    private static NavigableMap map5() {
+        TreeMap map = new TreeMap();
+        assertTrue(map.isEmpty());
+        map.put(zero, "Z");
+        map.put(one, "A");
+        map.put(five, "E");
+        map.put(three, "C");
+        map.put(two, "B");
+        map.put(four, "D");
+        map.put(seven, "F");
+        assertFalse(map.isEmpty());
+        assertEquals(7, map.size());
+        return map.subMap(one, true, seven, false);
+    }
+
+    private static NavigableMap map0() {
+        TreeMap map = new TreeMap();
+        assertTrue(map.isEmpty());
+        return map.tailMap(one, true);
+    }
+
+    /**
+     * Returns a new map from Integers -5 to -1 to Strings "A"-"E".
+     */
+    private static NavigableMap dmap5() {
+        TreeMap map = new TreeMap();
+        assertTrue(map.isEmpty());
+        map.put(m1, "A");
+        map.put(m5, "E");
+        map.put(m3, "C");
+        map.put(m2, "B");
+        map.put(m4, "D");
+        assertFalse(map.isEmpty());
+        assertEquals(5, map.size());
+        return map.descendingMap();
+    }
+
+    private static NavigableMap dmap0() {
+        TreeMap map = new TreeMap();
+        assertTrue(map.isEmpty());
+        return map;
+    }
+
+    /**
+     * clear removes all pairs
+     */
+    public void testClear() {
+        NavigableMap map = map5();
+        map.clear();
+        assertEquals(0, map.size());
+    }
+
+    /**
+     * Maps with same contents are equal
+     */
+    public void testEquals() {
+        NavigableMap map1 = map5();
+        NavigableMap map2 = map5();
+        assertEquals(map1, map2);
+        assertEquals(map2, map1);
+        map1.clear();
+        assertFalse(map1.equals(map2));
+        assertFalse(map2.equals(map1));
+    }
+
+    /**
+     * containsKey returns true for contained key
+     */
+    public void testContainsKey() {
+        NavigableMap map = map5();
+        assertTrue(map.containsKey(one));
+        assertFalse(map.containsKey(zero));
+    }
+
+    /**
+     * containsValue returns true for held values
+     */
+    public void testContainsValue() {
+        NavigableMap map = map5();
+        assertTrue(map.containsValue("A"));
+        assertFalse(map.containsValue("Z"));
+    }
+
+    /**
+     * get returns the correct element at the given key,
+     * or null if not present
+     */
+    public void testGet() {
+        NavigableMap map = map5();
+        assertEquals("A", (String)map.get(one));
+        NavigableMap empty = map0();
+        assertNull(empty.get(one));
+    }
+
+    /**
+     * isEmpty is true of empty map and false for non-empty
+     */
+    public void testIsEmpty() {
+        NavigableMap empty = map0();
+        NavigableMap map = map5();
+        assertTrue(empty.isEmpty());
+        assertFalse(map.isEmpty());
+    }
+
+    /**
+     * firstKey returns first key
+     */
+    public void testFirstKey() {
+        NavigableMap map = map5();
+        assertEquals(one, map.firstKey());
+    }
+
+    /**
+     * lastKey returns last key
+     */
+    public void testLastKey() {
+        NavigableMap map = map5();
+        assertEquals(five, map.lastKey());
+    }
+
+    /**
+     * keySet returns a Set containing all the keys
+     */
+    public void testKeySet() {
+        NavigableMap map = map5();
+        Set s = map.keySet();
+        assertEquals(5, s.size());
+        assertTrue(s.contains(one));
+        assertTrue(s.contains(two));
+        assertTrue(s.contains(three));
+        assertTrue(s.contains(four));
+        assertTrue(s.contains(five));
+    }
+
+    /**
+     * keySet is ordered
+     */
+    public void testKeySetOrder() {
+        NavigableMap map = map5();
+        Set s = map.keySet();
+        Iterator i = s.iterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, one);
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) < 0);
+            last = k;
+        }
+    }
+
+    /**
+     * values collection contains all values
+     */
+    public void testValues() {
+        NavigableMap map = map5();
+        Collection s = map.values();
+        assertEquals(5, s.size());
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * entrySet contains all pairs
+     */
+    public void testEntrySet() {
+        NavigableMap map = map5();
+        Set s = map.entrySet();
+        assertEquals(5, s.size());
+        Iterator it = s.iterator();
+        while (it.hasNext()) {
+            Map.Entry e = (Map.Entry) it.next();
+            assertTrue(
+                       (e.getKey().equals(one) && e.getValue().equals("A")) ||
+                       (e.getKey().equals(two) && e.getValue().equals("B")) ||
+                       (e.getKey().equals(three) && e.getValue().equals("C")) ||
+                       (e.getKey().equals(four) && e.getValue().equals("D")) ||
+                       (e.getKey().equals(five) && e.getValue().equals("E")));
+        }
+    }
+
+    /**
+     * putAll adds all key-value pairs from the given map
+     */
+    public void testPutAll() {
+        NavigableMap empty = map0();
+        NavigableMap map = map5();
+        empty.putAll(map);
+        assertEquals(5, empty.size());
+        assertTrue(empty.containsKey(one));
+        assertTrue(empty.containsKey(two));
+        assertTrue(empty.containsKey(three));
+        assertTrue(empty.containsKey(four));
+        assertTrue(empty.containsKey(five));
+    }
+
+    /**
+     * remove removes the correct key-value pair from the map
+     */
+    public void testRemove() {
+        NavigableMap map = map5();
+        map.remove(five);
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(five));
+    }
+
+    /**
+     * lowerEntry returns preceding entry.
+     */
+    public void testLowerEntry() {
+        NavigableMap map = map5();
+        Map.Entry e1 = map.lowerEntry(three);
+        assertEquals(two, e1.getKey());
+
+        Map.Entry e2 = map.lowerEntry(six);
+        assertEquals(five, e2.getKey());
+
+        Map.Entry e3 = map.lowerEntry(one);
+        assertNull(e3);
+
+        Map.Entry e4 = map.lowerEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higherEntry returns next entry.
+     */
+    public void testHigherEntry() {
+        NavigableMap map = map5();
+        Map.Entry e1 = map.higherEntry(three);
+        assertEquals(four, e1.getKey());
+
+        Map.Entry e2 = map.higherEntry(zero);
+        assertEquals(one, e2.getKey());
+
+        Map.Entry e3 = map.higherEntry(five);
+        assertNull(e3);
+
+        Map.Entry e4 = map.higherEntry(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floorEntry returns preceding entry.
+     */
+    public void testFloorEntry() {
+        NavigableMap map = map5();
+        Map.Entry e1 = map.floorEntry(three);
+        assertEquals(three, e1.getKey());
+
+        Map.Entry e2 = map.floorEntry(six);
+        assertEquals(five, e2.getKey());
+
+        Map.Entry e3 = map.floorEntry(one);
+        assertEquals(one, e3.getKey());
+
+        Map.Entry e4 = map.floorEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceilingEntry returns next entry.
+     */
+    public void testCeilingEntry() {
+        NavigableMap map = map5();
+        Map.Entry e1 = map.ceilingEntry(three);
+        assertEquals(three, e1.getKey());
+
+        Map.Entry e2 = map.ceilingEntry(zero);
+        assertEquals(one, e2.getKey());
+
+        Map.Entry e3 = map.ceilingEntry(five);
+        assertEquals(five, e3.getKey());
+
+        Map.Entry e4 = map.ceilingEntry(six);
+        assertNull(e4);
+    }
+
+    /**
+     * pollFirstEntry returns entries in order
+     */
+    public void testPollFirstEntry() {
+        NavigableMap map = map5();
+        Map.Entry e = map.pollFirstEntry();
+        assertEquals(one, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(two, e.getKey());
+        map.put(one, "A");
+        e = map.pollFirstEntry();
+        assertEquals(one, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(three, e.getKey());
+        map.remove(four);
+        e = map.pollFirstEntry();
+        assertEquals(five, e.getKey());
+        try {
+            e.setValue("A");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        assertTrue(map.isEmpty());
+        Map.Entry f = map.firstEntry();
+        assertNull(f);
+        e = map.pollFirstEntry();
+        assertNull(e);
+    }
+
+    /**
+     * pollLastEntry returns entries in order
+     */
+    public void testPollLastEntry() {
+        NavigableMap map = map5();
+        Map.Entry e = map.pollLastEntry();
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(four, e.getKey());
+        map.put(five, "E");
+        e = map.pollLastEntry();
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(three, e.getKey());
+        map.remove(two);
+        e = map.pollLastEntry();
+        assertEquals(one, e.getKey());
+        try {
+            e.setValue("E");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollLastEntry();
+        assertNull(e);
+    }
+
+    /**
+     * size returns the correct values
+     */
+    public void testSize() {
+        NavigableMap map = map5();
+        NavigableMap empty = map0();
+        assertEquals(0, empty.size());
+        assertEquals(5, map.size());
+    }
+
+    /**
+     * toString contains toString of elements
+     */
+    public void testToString() {
+        NavigableMap map = map5();
+        String s = map.toString();
+        for (int i = 1; i <= 5; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    // Exception tests
+
+    /**
+     * get(null) of nonempty map throws NPE
+     */
+    public void testGet_NullPointerException() {
+        NavigableMap c = map5();
+        try {
+            c.get(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsKey(null) of nonempty map throws NPE
+     */
+    public void testContainsKey_NullPointerException() {
+        NavigableMap c = map5();
+        try {
+            c.containsKey(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * put(null,x) throws NPE
+     */
+    public void testPut1_NullPointerException() {
+        NavigableMap c = map5();
+        try {
+            c.put(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null) throws NPE
+     */
+    public void testRemove1_NullPointerException() {
+        NavigableMap c = map5();
+        try {
+            c.remove(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * A deserialized map equals original
+     */
+    public void testSerialization() throws Exception {
+        NavigableMap x = map5();
+        NavigableMap y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    /**
+     * subMap returns map with keys in requested range
+     */
+    public void testSubMapContents() {
+        NavigableMap map = map5();
+        SortedMap sm = map.subMap(two, four);
+        assertEquals(two, sm.firstKey());
+        assertEquals(three, sm.lastKey());
+        assertEquals(2, sm.size());
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(two));
+        assertEquals(4, map.size());
+        assertEquals(1, sm.size());
+        assertEquals(three, sm.firstKey());
+        assertEquals(three, sm.lastKey());
+        assertEquals("C", sm.remove(three));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, map.size());
+    }
+
+    public void testSubMapContents2() {
+        NavigableMap map = map5();
+        SortedMap sm = map.subMap(two, three);
+        assertEquals(1, sm.size());
+        assertEquals(two, sm.firstKey());
+        assertEquals(two, sm.lastKey());
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertFalse(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(two));
+        assertEquals(4, map.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertSame(sm.remove(three), null);
+        assertEquals(4, map.size());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testHeadMapContents() {
+        NavigableMap map = map5();
+        SortedMap sm = map.headMap(four);
+        assertTrue(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(one, k);
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, map.size());
+        assertEquals(four, map.firstKey());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testTailMapContents() {
+        NavigableMap map = map5();
+        SortedMap sm = map.tailMap(two);
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertTrue(sm.containsKey(four));
+        assertTrue(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        k = (Integer)(i.next());
+        assertEquals(four, k);
+        k = (Integer)(i.next());
+        assertEquals(five, k);
+        assertFalse(i.hasNext());
+
+        Iterator ei = sm.entrySet().iterator();
+        Map.Entry e;
+        e = (Map.Entry)(ei.next());
+        assertEquals(two, e.getKey());
+        assertEquals("B", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(three, e.getKey());
+        assertEquals("C", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(four, e.getKey());
+        assertEquals("D", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        assertFalse(i.hasNext());
+
+        SortedMap ssm = sm.tailMap(four);
+        assertEquals(four, ssm.firstKey());
+        assertEquals(five, ssm.lastKey());
+        assertEquals("D", ssm.remove(four));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, map.size());
+    }
+
+    /**
+     * clear removes all pairs
+     */
+    public void testDescendingClear() {
+        NavigableMap map = dmap5();
+        map.clear();
+        assertEquals(0, map.size());
+    }
+
+    /**
+     * Maps with same contents are equal
+     */
+    public void testDescendingEquals() {
+        NavigableMap map1 = dmap5();
+        NavigableMap map2 = dmap5();
+        assertEquals(map1, map2);
+        assertEquals(map2, map1);
+        map1.clear();
+        assertFalse(map1.equals(map2));
+        assertFalse(map2.equals(map1));
+    }
+
+    /**
+     * containsKey returns true for contained key
+     */
+    public void testDescendingContainsKey() {
+        NavigableMap map = dmap5();
+        assertTrue(map.containsKey(m1));
+        assertFalse(map.containsKey(zero));
+    }
+
+    /**
+     * containsValue returns true for held values
+     */
+    public void testDescendingContainsValue() {
+        NavigableMap map = dmap5();
+        assertTrue(map.containsValue("A"));
+        assertFalse(map.containsValue("Z"));
+    }
+
+    /**
+     * get returns the correct element at the given key,
+     * or null if not present
+     */
+    public void testDescendingGet() {
+        NavigableMap map = dmap5();
+        assertEquals("A", (String)map.get(m1));
+        NavigableMap empty = dmap0();
+        assertNull(empty.get(m1));
+    }
+
+    /**
+     * isEmpty is true of empty map and false for non-empty
+     */
+    public void testDescendingIsEmpty() {
+        NavigableMap empty = dmap0();
+        NavigableMap map = dmap5();
+        assertTrue(empty.isEmpty());
+        assertFalse(map.isEmpty());
+    }
+
+    /**
+     * firstKey returns first key
+     */
+    public void testDescendingFirstKey() {
+        NavigableMap map = dmap5();
+        assertEquals(m1, map.firstKey());
+    }
+
+    /**
+     * lastKey returns last key
+     */
+    public void testDescendingLastKey() {
+        NavigableMap map = dmap5();
+        assertEquals(m5, map.lastKey());
+    }
+
+    /**
+     * keySet returns a Set containing all the keys
+     */
+    public void testDescendingKeySet() {
+        NavigableMap map = dmap5();
+        Set s = map.keySet();
+        assertEquals(5, s.size());
+        assertTrue(s.contains(m1));
+        assertTrue(s.contains(m2));
+        assertTrue(s.contains(m3));
+        assertTrue(s.contains(m4));
+        assertTrue(s.contains(m5));
+    }
+
+    /**
+     * keySet is ordered
+     */
+    public void testDescendingKeySetOrder() {
+        NavigableMap map = dmap5();
+        Set s = map.keySet();
+        Iterator i = s.iterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, m1);
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) > 0);
+            last = k;
+        }
+    }
+
+    /**
+     * values collection contains all values
+     */
+    public void testDescendingValues() {
+        NavigableMap map = dmap5();
+        Collection s = map.values();
+        assertEquals(5, s.size());
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * keySet.toArray returns contains all keys
+     */
+    public void testDescendingAscendingKeySetToArray() {
+        NavigableMap map = dmap5();
+        Set s = map.keySet();
+        Object[] ar = s.toArray();
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        assertEquals(5, ar.length);
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * descendingkeySet.toArray returns contains all keys
+     */
+    public void testDescendingDescendingKeySetToArray() {
+        NavigableMap map = dmap5();
+        Set s = map.descendingKeySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * Values.toArray contains all values
+     */
+    public void testDescendingValuesToArray() {
+        NavigableMap map = dmap5();
+        Collection v = map.values();
+        Object[] ar = v.toArray();
+        ArrayList s = new ArrayList(Arrays.asList(ar));
+        assertEquals(5, ar.length);
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * entrySet contains all pairs
+     */
+    public void testDescendingEntrySet() {
+        NavigableMap map = dmap5();
+        Set s = map.entrySet();
+        assertEquals(5, s.size());
+        Iterator it = s.iterator();
+        while (it.hasNext()) {
+            Map.Entry e = (Map.Entry) it.next();
+            assertTrue(
+                       (e.getKey().equals(m1) && e.getValue().equals("A")) ||
+                       (e.getKey().equals(m2) && e.getValue().equals("B")) ||
+                       (e.getKey().equals(m3) && e.getValue().equals("C")) ||
+                       (e.getKey().equals(m4) && e.getValue().equals("D")) ||
+                       (e.getKey().equals(m5) && e.getValue().equals("E")));
+        }
+    }
+
+    /**
+     * putAll adds all key-value pairs from the given map
+     */
+    public void testDescendingPutAll() {
+        NavigableMap empty = dmap0();
+        NavigableMap map = dmap5();
+        empty.putAll(map);
+        assertEquals(5, empty.size());
+        assertTrue(empty.containsKey(m1));
+        assertTrue(empty.containsKey(m2));
+        assertTrue(empty.containsKey(m3));
+        assertTrue(empty.containsKey(m4));
+        assertTrue(empty.containsKey(m5));
+    }
+
+    /**
+     * remove removes the correct key-value pair from the map
+     */
+    public void testDescendingRemove() {
+        NavigableMap map = dmap5();
+        map.remove(m5);
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(m5));
+    }
+
+    /**
+     * lowerEntry returns preceding entry.
+     */
+    public void testDescendingLowerEntry() {
+        NavigableMap map = dmap5();
+        Map.Entry e1 = map.lowerEntry(m3);
+        assertEquals(m2, e1.getKey());
+
+        Map.Entry e2 = map.lowerEntry(m6);
+        assertEquals(m5, e2.getKey());
+
+        Map.Entry e3 = map.lowerEntry(m1);
+        assertNull(e3);
+
+        Map.Entry e4 = map.lowerEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higherEntry returns next entry.
+     */
+    public void testDescendingHigherEntry() {
+        NavigableMap map = dmap5();
+        Map.Entry e1 = map.higherEntry(m3);
+        assertEquals(m4, e1.getKey());
+
+        Map.Entry e2 = map.higherEntry(zero);
+        assertEquals(m1, e2.getKey());
+
+        Map.Entry e3 = map.higherEntry(m5);
+        assertNull(e3);
+
+        Map.Entry e4 = map.higherEntry(m6);
+        assertNull(e4);
+    }
+
+    /**
+     * floorEntry returns preceding entry.
+     */
+    public void testDescendingFloorEntry() {
+        NavigableMap map = dmap5();
+        Map.Entry e1 = map.floorEntry(m3);
+        assertEquals(m3, e1.getKey());
+
+        Map.Entry e2 = map.floorEntry(m6);
+        assertEquals(m5, e2.getKey());
+
+        Map.Entry e3 = map.floorEntry(m1);
+        assertEquals(m1, e3.getKey());
+
+        Map.Entry e4 = map.floorEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceilingEntry returns next entry.
+     */
+    public void testDescendingCeilingEntry() {
+        NavigableMap map = dmap5();
+        Map.Entry e1 = map.ceilingEntry(m3);
+        assertEquals(m3, e1.getKey());
+
+        Map.Entry e2 = map.ceilingEntry(zero);
+        assertEquals(m1, e2.getKey());
+
+        Map.Entry e3 = map.ceilingEntry(m5);
+        assertEquals(m5, e3.getKey());
+
+        Map.Entry e4 = map.ceilingEntry(m6);
+        assertNull(e4);
+    }
+
+    /**
+     * pollFirstEntry returns entries in order
+     */
+    public void testDescendingPollFirstEntry() {
+        NavigableMap map = dmap5();
+        Map.Entry e = map.pollFirstEntry();
+        assertEquals(m1, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(m2, e.getKey());
+        map.put(m1, "A");
+        e = map.pollFirstEntry();
+        assertEquals(m1, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(m3, e.getKey());
+        map.remove(m4);
+        e = map.pollFirstEntry();
+        assertEquals(m5, e.getKey());
+        try {
+            e.setValue("A");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollFirstEntry();
+        assertNull(e);
+    }
+
+    /**
+     * pollLastEntry returns entries in order
+     */
+    public void testDescendingPollLastEntry() {
+        NavigableMap map = dmap5();
+        Map.Entry e = map.pollLastEntry();
+        assertEquals(m5, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(m4, e.getKey());
+        map.put(m5, "E");
+        e = map.pollLastEntry();
+        assertEquals(m5, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(m3, e.getKey());
+        map.remove(m2);
+        e = map.pollLastEntry();
+        assertEquals(m1, e.getKey());
+        try {
+            e.setValue("E");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollLastEntry();
+        assertNull(e);
+    }
+
+    /**
+     * size returns the correct values
+     */
+    public void testDescendingSize() {
+        NavigableMap map = dmap5();
+        NavigableMap empty = dmap0();
+        assertEquals(0, empty.size());
+        assertEquals(5, map.size());
+    }
+
+    /**
+     * toString contains toString of elements
+     */
+    public void testDescendingToString() {
+        NavigableMap map = dmap5();
+        String s = map.toString();
+        for (int i = 1; i <= 5; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    // Exception testDescendings
+
+    /**
+     * get(null) of nonempty map throws NPE
+     */
+    public void testDescendingGet_NullPointerException() {
+        NavigableMap c = dmap5();
+        try {
+            c.get(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * put(null,x) throws NPE
+     */
+    public void testDescendingPut1_NullPointerException() {
+        NavigableMap c = dmap5();
+        try {
+            c.put(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * A deserialized map equals original
+     */
+    public void testDescendingSerialization() throws Exception {
+        NavigableMap x = dmap5();
+        NavigableMap y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    /**
+     * subMap returns map with keys in requested range
+     */
+    public void testDescendingSubMapContents() {
+        NavigableMap map = dmap5();
+        SortedMap sm = map.subMap(m2, m4);
+        assertEquals(m2, sm.firstKey());
+        assertEquals(m3, sm.lastKey());
+        assertEquals(2, sm.size());
+        assertFalse(sm.containsKey(m1));
+        assertTrue(sm.containsKey(m2));
+        assertTrue(sm.containsKey(m3));
+        assertFalse(sm.containsKey(m4));
+        assertFalse(sm.containsKey(m5));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(m2));
+        assertEquals(4, map.size());
+        assertEquals(1, sm.size());
+        assertEquals(m3, sm.firstKey());
+        assertEquals(m3, sm.lastKey());
+        assertEquals("C", sm.remove(m3));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, map.size());
+    }
+
+    public void testDescendingSubMapContents2() {
+        NavigableMap map = dmap5();
+        SortedMap sm = map.subMap(m2, m3);
+        assertEquals(1, sm.size());
+        assertEquals(m2, sm.firstKey());
+        assertEquals(m2, sm.lastKey());
+        assertFalse(sm.containsKey(m1));
+        assertTrue(sm.containsKey(m2));
+        assertFalse(sm.containsKey(m3));
+        assertFalse(sm.containsKey(m4));
+        assertFalse(sm.containsKey(m5));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(m2));
+        assertEquals(4, map.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertSame(sm.remove(m3), null);
+        assertEquals(4, map.size());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testDescendingHeadMapContents() {
+        NavigableMap map = dmap5();
+        SortedMap sm = map.headMap(m4);
+        assertTrue(sm.containsKey(m1));
+        assertTrue(sm.containsKey(m2));
+        assertTrue(sm.containsKey(m3));
+        assertFalse(sm.containsKey(m4));
+        assertFalse(sm.containsKey(m5));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m1, k);
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, map.size());
+        assertEquals(m4, map.firstKey());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testDescendingTailMapContents() {
+        NavigableMap map = dmap5();
+        SortedMap sm = map.tailMap(m2);
+        assertFalse(sm.containsKey(m1));
+        assertTrue(sm.containsKey(m2));
+        assertTrue(sm.containsKey(m3));
+        assertTrue(sm.containsKey(m4));
+        assertTrue(sm.containsKey(m5));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        k = (Integer)(i.next());
+        assertEquals(m4, k);
+        k = (Integer)(i.next());
+        assertEquals(m5, k);
+        assertFalse(i.hasNext());
+
+        Iterator ei = sm.entrySet().iterator();
+        Map.Entry e;
+        e = (Map.Entry)(ei.next());
+        assertEquals(m2, e.getKey());
+        assertEquals("B", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(m3, e.getKey());
+        assertEquals("C", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(m4, e.getKey());
+        assertEquals("D", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(m5, e.getKey());
+        assertEquals("E", e.getValue());
+        assertFalse(i.hasNext());
+
+        SortedMap ssm = sm.tailMap(m4);
+        assertEquals(m4, ssm.firstKey());
+        assertEquals(m5, ssm.lastKey());
+        assertEquals("D", ssm.remove(m4));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, map.size());
+    }
+
+}
diff --git a/jdk/test/java/util/concurrent/tck/TreeSubSetTest.java b/jdk/test/java/util/concurrent/tck/TreeSubSetTest.java
new file mode 100644
index 00000000000..afc0604075d
--- /dev/null
+++ b/jdk/test/java/util/concurrent/tck/TreeSubSetTest.java
@@ -0,0 +1,1139 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.NavigableSet;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class TreeSubSetTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(TreeSubSetTest.class);
+    }
+
+    static class MyReverseComparator implements Comparator {
+        public int compare(Object x, Object y) {
+            return ((Comparable)y).compareTo(x);
+        }
+    }
+
+    /**
+     * Returns a new set of given size containing consecutive
+     * Integers 0 ... n.
+     */
+    private NavigableSet<Integer> populatedSet(int n) {
+        TreeSet<Integer> q = new TreeSet<Integer>();
+        assertTrue(q.isEmpty());
+
+        for (int i = n - 1; i >= 0; i -= 2)
+            assertTrue(q.add(new Integer(i)));
+        for (int i = (n & 1); i < n; i += 2)
+            assertTrue(q.add(new Integer(i)));
+        assertTrue(q.add(new Integer(-n)));
+        assertTrue(q.add(new Integer(n)));
+        NavigableSet s = q.subSet(new Integer(0), true, new Integer(n), false);
+        assertFalse(s.isEmpty());
+        assertEquals(n, s.size());
+        return s;
+    }
+
+    /**
+     * Returns a new set of first 5 ints.
+     */
+    private NavigableSet set5() {
+        TreeSet q = new TreeSet();
+        assertTrue(q.isEmpty());
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        q.add(four);
+        q.add(five);
+        q.add(zero);
+        q.add(seven);
+        NavigableSet s = q.subSet(one, true, seven, false);
+        assertEquals(5, s.size());
+        return s;
+    }
+
+    private NavigableSet dset5() {
+        TreeSet q = new TreeSet();
+        assertTrue(q.isEmpty());
+        q.add(m1);
+        q.add(m2);
+        q.add(m3);
+        q.add(m4);
+        q.add(m5);
+        NavigableSet s = q.descendingSet();
+        assertEquals(5, s.size());
+        return s;
+    }
+
+    private static NavigableSet set0() {
+        TreeSet set = new TreeSet();
+        assertTrue(set.isEmpty());
+        return set.tailSet(m1, false);
+    }
+
+    private static NavigableSet dset0() {
+        TreeSet set = new TreeSet();
+        assertTrue(set.isEmpty());
+        return set;
+    }
+
+    /**
+     * A new set has unbounded capacity
+     */
+    public void testConstructor1() {
+        assertEquals(0, set0().size());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        NavigableSet q = set0();
+        assertTrue(q.isEmpty());
+        assertTrue(q.add(new Integer(1)));
+        assertFalse(q.isEmpty());
+        assertTrue(q.add(new Integer(2)));
+        q.pollFirst();
+        q.pollFirst();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testSize() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i, q.size());
+            q.pollFirst();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * add(null) throws NPE
+     */
+    public void testAddNull() {
+        NavigableSet q = set0();
+        try {
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Add of comparable element succeeds
+     */
+    public void testAdd() {
+        NavigableSet q = set0();
+        assertTrue(q.add(six));
+    }
+
+    /**
+     * Add of duplicate element fails
+     */
+    public void testAddDup() {
+        NavigableSet q = set0();
+        assertTrue(q.add(six));
+        assertFalse(q.add(six));
+    }
+
+    /**
+     * Add of non-Comparable throws CCE
+     */
+    public void testAddNonComparable() {
+        NavigableSet q = set0();
+        try {
+            q.add(new Object());
+            q.add(new Object());
+            shouldThrow();
+        } catch (ClassCastException success) {}
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        NavigableSet q = set0();
+        try {
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testAddAll2() {
+        NavigableSet q = set0();
+        Integer[] ints = new Integer[SIZE];
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        NavigableSet q = set0();
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i + SIZE);
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Set contains all elements of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(SIZE - 1 - i);
+        NavigableSet q = set0();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(new Integer(i), q.pollFirst());
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testRemoveElement() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertTrue(q.contains(i - 1));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertFalse(q.remove(i + 1));
+            assertFalse(q.contains(i + 1));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.pollFirst();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        NavigableSet q = populatedSet(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        assertTrue(q.add(new Integer(1)));
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        NavigableSet q = populatedSet(SIZE);
+        NavigableSet p = set0();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        NavigableSet q = populatedSet(SIZE);
+        NavigableSet p = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.pollFirst();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            NavigableSet q = populatedSet(SIZE);
+            NavigableSet p = populatedSet(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer x = (Integer)(p.pollFirst());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    /**
+     * lower returns preceding element
+     */
+    public void testLower() {
+        NavigableSet q = set5();
+        Object e1 = q.lower(three);
+        assertEquals(two, e1);
+
+        Object e2 = q.lower(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.lower(one);
+        assertNull(e3);
+
+        Object e4 = q.lower(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higher returns next element
+     */
+    public void testHigher() {
+        NavigableSet q = set5();
+        Object e1 = q.higher(three);
+        assertEquals(four, e1);
+
+        Object e2 = q.higher(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.higher(five);
+        assertNull(e3);
+
+        Object e4 = q.higher(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floor returns preceding element
+     */
+    public void testFloor() {
+        NavigableSet q = set5();
+        Object e1 = q.floor(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.floor(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.floor(one);
+        assertEquals(one, e3);
+
+        Object e4 = q.floor(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceiling returns next element
+     */
+    public void testCeiling() {
+        NavigableSet q = set5();
+        Object e1 = q.ceiling(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.ceiling(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.ceiling(five);
+        assertEquals(five, e3);
+
+        Object e4 = q.ceiling(six);
+        assertNull(e4);
+    }
+
+    /**
+     * toArray contains all elements in sorted order
+     */
+    public void testToArray() {
+        NavigableSet q = populatedSet(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.pollFirst());
+    }
+
+    /**
+     * toArray(a) contains all elements in sorted order
+     */
+    public void testToArray2() {
+        NavigableSet<Integer> q = populatedSet(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.pollFirst());
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() {
+        NavigableSet q = populatedSet(SIZE);
+        Iterator it = q.iterator();
+        int i;
+        for (i = 0; it.hasNext(); i++)
+            assertTrue(q.contains(it.next()));
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty set has no elements
+     */
+    public void testEmptyIterator() {
+        assertIteratorExhausted(set0().iterator());
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final NavigableSet q = set0();
+        q.add(new Integer(2));
+        q.add(new Integer(1));
+        q.add(new Integer(3));
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertEquals(2, it.next());
+        assertEquals(3, it.next());
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        NavigableSet q = populatedSet(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized set has same elements
+     */
+    public void testSerialization() throws Exception {
+        NavigableSet x = populatedSet(SIZE);
+        NavigableSet y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x, y);
+        assertEquals(y, x);
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.pollFirst(), y.pollFirst());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * subSet returns set with keys in requested range
+     */
+    public void testSubSetContents() {
+        NavigableSet set = set5();
+        SortedSet sm = set.subSet(two, four);
+        assertEquals(two, sm.first());
+        assertEquals(three, sm.last());
+        assertEquals(2, sm.size());
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(two));
+        assertEquals(4, set.size());
+        assertEquals(1, sm.size());
+        assertEquals(three, sm.first());
+        assertEquals(three, sm.last());
+        assertTrue(sm.remove(three));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, set.size());
+    }
+
+    public void testSubSetContents2() {
+        NavigableSet set = set5();
+        SortedSet sm = set.subSet(two, three);
+        assertEquals(1, sm.size());
+        assertEquals(two, sm.first());
+        assertEquals(two, sm.last());
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertFalse(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(two));
+        assertEquals(4, set.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertFalse(sm.remove(three));
+        assertEquals(4, set.size());
+    }
+
+    /**
+     * headSet returns set with keys in requested range
+     */
+    public void testHeadSetContents() {
+        NavigableSet set = set5();
+        SortedSet sm = set.headSet(four);
+        assertTrue(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(one, k);
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, set.size());
+        assertEquals(four, set.first());
+    }
+
+    /**
+     * tailSet returns set with keys in requested range
+     */
+    public void testTailSetContents() {
+        NavigableSet set = set5();
+        SortedSet sm = set.tailSet(two);
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertTrue(sm.contains(four));
+        assertTrue(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        k = (Integer)(i.next());
+        assertEquals(four, k);
+        k = (Integer)(i.next());
+        assertEquals(five, k);
+        assertFalse(i.hasNext());
+
+        SortedSet ssm = sm.tailSet(four);
+        assertEquals(four, ssm.first());
+        assertEquals(five, ssm.last());
+        assertTrue(ssm.remove(four));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, set.size());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testDescendingSize() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i, q.size());
+            q.pollFirst();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * Add of comparable element succeeds
+     */
+    public void testDescendingAdd() {
+        NavigableSet q = dset0();
+        assertTrue(q.add(m6));
+    }
+
+    /**
+     * Add of duplicate element fails
+     */
+    public void testDescendingAddDup() {
+        NavigableSet q = dset0();
+        assertTrue(q.add(m6));
+        assertFalse(q.add(m6));
+    }
+
+    /**
+     * Add of non-Comparable throws CCE
+     */
+    public void testDescendingAddNonComparable() {
+        NavigableSet q = dset0();
+        try {
+            q.add(new Object());
+            q.add(new Object());
+            shouldThrow();
+        } catch (ClassCastException success) {}
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testDescendingAddAll1() {
+        NavigableSet q = dset0();
+        try {
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testDescendingAddAll2() {
+        NavigableSet q = dset0();
+        Integer[] ints = new Integer[SIZE];
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testDescendingAddAll3() {
+        NavigableSet q = dset0();
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i + SIZE);
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Set contains all elements of successful addAll
+     */
+    public void testDescendingAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(SIZE - 1 - i);
+        NavigableSet q = dset0();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(new Integer(i), q.pollFirst());
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testDescendingPoll() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testDescendingRemoveElement() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.remove(new Integer(i)));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.remove(new Integer(i)));
+            assertFalse(q.remove(new Integer(i + 1)));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testDescendingContains() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.pollFirst();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testDescendingClear() {
+        NavigableSet q = populatedSet(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        assertTrue(q.add(new Integer(1)));
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testDescendingContainsAll() {
+        NavigableSet q = populatedSet(SIZE);
+        NavigableSet p = dset0();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testDescendingRetainAll() {
+        NavigableSet q = populatedSet(SIZE);
+        NavigableSet p = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.pollFirst();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testDescendingRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            NavigableSet q = populatedSet(SIZE);
+            NavigableSet p = populatedSet(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer x = (Integer)(p.pollFirst());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    /**
+     * lower returns preceding element
+     */
+    public void testDescendingLower() {
+        NavigableSet q = dset5();
+        Object e1 = q.lower(m3);
+        assertEquals(m2, e1);
+
+        Object e2 = q.lower(m6);
+        assertEquals(m5, e2);
+
+        Object e3 = q.lower(m1);
+        assertNull(e3);
+
+        Object e4 = q.lower(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higher returns next element
+     */
+    public void testDescendingHigher() {
+        NavigableSet q = dset5();
+        Object e1 = q.higher(m3);
+        assertEquals(m4, e1);
+
+        Object e2 = q.higher(zero);
+        assertEquals(m1, e2);
+
+        Object e3 = q.higher(m5);
+        assertNull(e3);
+
+        Object e4 = q.higher(m6);
+        assertNull(e4);
+    }
+
+    /**
+     * floor returns preceding element
+     */
+    public void testDescendingFloor() {
+        NavigableSet q = dset5();
+        Object e1 = q.floor(m3);
+        assertEquals(m3, e1);
+
+        Object e2 = q.floor(m6);
+        assertEquals(m5, e2);
+
+        Object e3 = q.floor(m1);
+        assertEquals(m1, e3);
+
+        Object e4 = q.floor(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceiling returns next element
+     */
+    public void testDescendingCeiling() {
+        NavigableSet q = dset5();
+        Object e1 = q.ceiling(m3);
+        assertEquals(m3, e1);
+
+        Object e2 = q.ceiling(zero);
+        assertEquals(m1, e2);
+
+        Object e3 = q.ceiling(m5);
+        assertEquals(m5, e3);
+
+        Object e4 = q.ceiling(m6);
+        assertNull(e4);
+    }
+
+    /**
+     * toArray contains all elements
+     */
+    public void testDescendingToArray() {
+        NavigableSet q = populatedSet(SIZE);
+        Object[] o = q.toArray();
+        Arrays.sort(o);
+        for (int i = 0; i < o.length; i++)
+            assertEquals(o[i], q.pollFirst());
+    }
+
+    /**
+     * toArray(a) contains all elements
+     */
+    public void testDescendingToArray2() {
+        NavigableSet q = populatedSet(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        assertSame(ints, q.toArray(ints));
+        Arrays.sort(ints);
+        for (int i = 0; i < ints.length; i++)
+            assertEquals(ints[i], q.pollFirst());
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testDescendingIterator() {
+        NavigableSet q = populatedSet(SIZE);
+        int i = 0;
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+    }
+
+    /**
+     * iterator of empty set has no elements
+     */
+    public void testDescendingEmptyIterator() {
+        NavigableSet q = dset0();
+        int i = 0;
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(0, i);
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testDescendingIteratorRemove() {
+        final NavigableSet q = dset0();
+        q.add(new Integer(2));
+        q.add(new Integer(1));
+        q.add(new Integer(3));
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertEquals(2, it.next());
+        assertEquals(3, it.next());
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testDescendingToString() {
+        NavigableSet q = populatedSet(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized set has same elements
+     */
+    public void testDescendingSerialization() throws Exception {
+        NavigableSet x = dset5();
+        NavigableSet y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertEquals(x, y);
+        assertEquals(y, x);
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.pollFirst(), y.pollFirst());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * subSet returns set with keys in requested range
+     */
+    public void testDescendingSubSetContents() {
+        NavigableSet set = dset5();
+        SortedSet sm = set.subSet(m2, m4);
+        assertEquals(m2, sm.first());
+        assertEquals(m3, sm.last());
+        assertEquals(2, sm.size());
+        assertFalse(sm.contains(m1));
+        assertTrue(sm.contains(m2));
+        assertTrue(sm.contains(m3));
+        assertFalse(sm.contains(m4));
+        assertFalse(sm.contains(m5));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(m2));
+        assertEquals(4, set.size());
+        assertEquals(1, sm.size());
+        assertEquals(m3, sm.first());
+        assertEquals(m3, sm.last());
+        assertTrue(sm.remove(m3));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, set.size());
+    }
+
+    public void testDescendingSubSetContents2() {
+        NavigableSet set = dset5();
+        SortedSet sm = set.subSet(m2, m3);
+        assertEquals(1, sm.size());
+        assertEquals(m2, sm.first());
+        assertEquals(m2, sm.last());
+        assertFalse(sm.contains(m1));
+        assertTrue(sm.contains(m2));
+        assertFalse(sm.contains(m3));
+        assertFalse(sm.contains(m4));
+        assertFalse(sm.contains(m5));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(m2));
+        assertEquals(4, set.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertFalse(sm.remove(m3));
+        assertEquals(4, set.size());
+    }
+
+    /**
+     * headSet returns set with keys in requested range
+     */
+    public void testDescendingHeadSetContents() {
+        NavigableSet set = dset5();
+        SortedSet sm = set.headSet(m4);
+        assertTrue(sm.contains(m1));
+        assertTrue(sm.contains(m2));
+        assertTrue(sm.contains(m3));
+        assertFalse(sm.contains(m4));
+        assertFalse(sm.contains(m5));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m1, k);
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, set.size());
+        assertEquals(m4, set.first());
+    }
+
+    /**
+     * tailSet returns set with keys in requested range
+     */
+    public void testDescendingTailSetContents() {
+        NavigableSet set = dset5();
+        SortedSet sm = set.tailSet(m2);
+        assertFalse(sm.contains(m1));
+        assertTrue(sm.contains(m2));
+        assertTrue(sm.contains(m3));
+        assertTrue(sm.contains(m4));
+        assertTrue(sm.contains(m5));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        k = (Integer)(i.next());
+        assertEquals(m4, k);
+        k = (Integer)(i.next());
+        assertEquals(m5, k);
+        assertFalse(i.hasNext());
+
+        SortedSet ssm = sm.tailSet(m4);
+        assertEquals(m4, ssm.first());
+        assertEquals(m5, ssm.last());
+        assertTrue(ssm.remove(m4));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, set.size());
+    }
+
+    /**
+     * addAll is idempotent
+     */
+    public void testAddAll_idempotent() throws Exception {
+        Set x = populatedSet(SIZE);
+        Set y = new TreeSet(x);
+        y.addAll(x);
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+}
diff --git a/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/DoubleStreamTestDataProvider.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/DoubleStreamTestDataProvider.java
index 0eb7c874249..d9a87bb8115 100644
--- a/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/DoubleStreamTestDataProvider.java
+++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/DoubleStreamTestDataProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2016, 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
@@ -41,6 +41,7 @@ public class DoubleStreamTestDataProvider {
     private static final double[] pseudoRandom;
 
     private static final Object[][] testData;
+    private static final Object[][] testSmallData;
     private static final Object[][] spliteratorTestData;
 
     static {
@@ -78,11 +79,15 @@ public class DoubleStreamTestDataProvider {
 
     static {
         {
-            List<Object[]> list = new ArrayList<>();
+            List<Object[]> listSmall = new ArrayList<>();
+            List<Object[]> list1000 = new ArrayList<>();
+            List<Object[]> list = null;
             for (Object[] data : arrays) {
                 final Object name = data[0];
                 final double[] doubles = (double[]) data[1];
 
+                list = doubles.length >= 1000 ? list1000 : listSmall;
+
                 list.add(new Object[]{"array:" + name,
                         TestData.Factory.ofArray("array:" + name, doubles)});
 
@@ -93,7 +98,9 @@ public class DoubleStreamTestDataProvider {
                 list.add(new Object[]{"SpinedList:" + name,
                         TestData.Factory.ofSpinedBuffer("SpinedList:" + name, isl)});
             }
-            testData = list.toArray(new Object[0][]);
+            testSmallData = listSmall.toArray(new Object[0][]);
+            list1000.addAll(listSmall);
+            testData = list1000.toArray(new Object[0][]);
         }
 
         {
@@ -136,6 +143,11 @@ public class DoubleStreamTestDataProvider {
         return testData;
     }
 
+    @DataProvider(name = "DoubleStreamTestData.small")
+    public static Object[][] makeSmallDoubleStreamTestData() {
+        return testSmallData;
+    }
+
     // returns an array of (String name, Supplier<PrimitiveSpliterator<Double>>)
     @DataProvider(name = "DoubleSpliterator")
     public static Object[][] spliteratorProvider() {
diff --git a/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/IntStreamTestDataProvider.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/IntStreamTestDataProvider.java
index dded670d167..945cd9580e5 100644
--- a/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/IntStreamTestDataProvider.java
+++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/IntStreamTestDataProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2016, 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
@@ -41,6 +41,7 @@ public class IntStreamTestDataProvider {
     private static final int[] pseudoRandom;
 
     private static final Object[][] testData;
+    private static final Object[][] testSmallData;
     private static final Object[][] spliteratorTestData;
 
     static {
@@ -78,11 +79,15 @@ public class IntStreamTestDataProvider {
 
     static {
         {
-            List<Object[]> list = new ArrayList<>();
+            List<Object[]> listSmall = new ArrayList<>();
+            List<Object[]> list1000 = new ArrayList<>();
+            List<Object[]> list = null;
             for (Object[] data : arrays) {
                 final Object name = data[0];
                 final int[] ints = (int[]) data[1];
 
+                list = ints.length >= 1000 ? list1000 : listSmall;
+
                 list.add(new Object[]{"array:" +
                                       name, TestData.Factory.ofArray("array:" + name, ints)});
 
@@ -98,7 +103,9 @@ public class IntStreamTestDataProvider {
                 list.add(streamDataDescr("IntStream.rangeClosed(0,l): " + ints.length,
                                          () -> IntStream.rangeClosed(0, ints.length)));
             }
-            testData = list.toArray(new Object[0][]);
+            testSmallData = listSmall.toArray(new Object[0][]);
+            list1000.addAll(listSmall);
+            testData = list1000.toArray(new Object[0][]);
         }
 
         {
@@ -150,6 +157,11 @@ public class IntStreamTestDataProvider {
         return testData;
     }
 
+    @DataProvider(name = "IntStreamTestData.small")
+    public static Object[][] makeSmallIntStreamTestData() {
+        return testSmallData;
+    }
+
     // returns an array of (String name, Supplier<PrimitiveSpliterator<Integer>>)
     @DataProvider(name = "IntSpliterator")
     public static Object[][] spliteratorProvider() {
diff --git a/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LongStreamTestDataProvider.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LongStreamTestDataProvider.java
index 4ce7ae6d217..80b1dcc1019 100644
--- a/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LongStreamTestDataProvider.java
+++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LongStreamTestDataProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2016, 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
@@ -41,6 +41,7 @@ public class LongStreamTestDataProvider {
     private static final long[] pseudoRandom;
 
     private static final Object[][] testData;
+    private static final Object[][] testSmallData;
     private static final Object[][] spliteratorTestData;
 
     static {
@@ -78,11 +79,15 @@ public class LongStreamTestDataProvider {
 
     static {
         {
-            List<Object[]> list = new ArrayList<>();
+            List<Object[]> listSmall = new ArrayList<>();
+            List<Object[]> list1000 = new ArrayList<>();
+            List<Object[]> list = null;
             for (Object[] data : arrays) {
                 final Object name = data[0];
                 final long[] longs = (long[]) data[1];
 
+                list = longs.length >= 1000 ? list1000 : listSmall;
+
                 list.add(new Object[]{"array:" + name,
                         TestData.Factory.ofArray("array:" + name, longs)});
 
@@ -98,7 +103,9 @@ public class LongStreamTestDataProvider {
                 list.add(streamDataDescr("LongStream.longRangeClosed(0,l): " + longs.length,
                                          () -> LongStream.rangeClosed(0, longs.length)));
             }
-            testData = list.toArray(new Object[0][]);
+            testSmallData = listSmall.toArray(new Object[0][]);
+            list1000.addAll(listSmall);
+            testData = list1000.toArray(new Object[0][]);
         }
 
         {
@@ -150,6 +157,11 @@ public class LongStreamTestDataProvider {
         return testData;
     }
 
+    @DataProvider(name = "LongStreamTestData.small")
+    public static Object[][] makeSmallLongStreamTestData() {
+        return testSmallData;
+    }
+
     // returns an array of (String name, Supplier<PrimitiveSpliterator<Long>>)
     @DataProvider(name = "LongSpliterator")
     public static Object[][] spliteratorProvider() {
diff --git a/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/StreamTestDataProvider.java b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/StreamTestDataProvider.java
index 6f772f391ee..8a849015260 100644
--- a/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/StreamTestDataProvider.java
+++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/StreamTestDataProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2016, 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
@@ -46,6 +46,7 @@ public class StreamTestDataProvider {
     private static final Integer[] pseudoRandom;
 
     private static final Object[][] testData;
+    private static final Object[][] testSmallData;
     private static final Object[][] withNullTestData;
     private static final Object[][] spliteratorTestData;
 
@@ -84,12 +85,16 @@ public class StreamTestDataProvider {
 
     static {
         {
-            List<Object[]> list = new ArrayList<>();
+            List<Object[]> listSmall = new ArrayList<>();
+            List<Object[]> list1000 = new ArrayList<>();
+            List<Object[]> list = null;
             for (Object[] data : arrays) {
                 final Object name = data[0];
                 final Integer[] ints = (Integer[])data[1];
                 final List<Integer> intsAsList = Arrays.asList(ints);
 
+                list = ints.length >= 1000 ? list1000 : listSmall;
+
                 list.add(arrayDataDescr("array:" + name, ints));
                 list.add(collectionDataDescr("ArrayList.asList:" + name, intsAsList));
                 list.add(collectionDataDescr("ArrayList:" + name, new ArrayList<>(intsAsList)));
@@ -114,7 +119,9 @@ public class StreamTestDataProvider {
 
                 // @@@ Add more
             }
-            testData = list.toArray(new Object[0][]);
+            testSmallData = listSmall.toArray(new Object[0][]);
+            list1000.addAll(listSmall);
+            testData = list1000.toArray(new Object[0][]);
         }
 
         // Simple combination of numbers and null values, probably excessive but may catch
@@ -192,6 +199,11 @@ public class StreamTestDataProvider {
         return testData;
     }
 
+    @DataProvider(name = "StreamTestData<Integer>.small")
+    public static Object[][] makeSmallStreamTestData() {
+        return testSmallData;
+    }
+
     @DataProvider(name = "withNull:StreamTestData<Integer>")
     public static Object[][] makeStreamWithNullTestData() {
         return withNullTestData;
diff --git a/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/FlatMapOpTest.java b/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/FlatMapOpTest.java
index 453ec6e952c..afbedc871da 100644
--- a/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/FlatMapOpTest.java
+++ b/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/FlatMapOpTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2016, 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,7 +24,7 @@
 /*
  * @test
  * @summary flat-map operations
- * @bug 8044047
+ * @bug 8044047 8076458
  */
 
 package org.openjdk.tests.java.util.stream;
@@ -140,7 +140,10 @@ public class FlatMapOpTest extends OpTestCase {
 
         result = exerciseOps(data, s-> s.flatMap(e -> Stream.empty()));
         assertEquals(0, result.size());
+    }
 
+    @Test(dataProvider = "StreamTestData<Integer>.small", dataProviderClass = StreamTestDataProvider.class)
+    public void testOpsX(String name, TestData.OfRef<Integer> data) {
         exerciseOps(data, s -> s.flatMap(mfLt));
         exerciseOps(data, s -> s.flatMap(integerRangeMapper));
         exerciseOps(data, s -> s.flatMap((Integer e) -> IntStream.range(0, e).boxed().limit(10)));
@@ -156,7 +159,10 @@ public class FlatMapOpTest extends OpTestCase {
 
         result = exerciseOps(data, s -> s.flatMap(i -> IntStream.empty()));
         assertEquals(0, result.size());
+    }
 
+    @Test(dataProvider = "IntStreamTestData.small", dataProviderClass = IntStreamTestDataProvider.class)
+    public void testIntOpsX(String name, TestData.OfInt data) {
         exerciseOps(data, s -> s.flatMap(e -> IntStream.range(0, e)));
         exerciseOps(data, s -> s.flatMap(e -> IntStream.range(0, e).limit(10)));
     }
@@ -171,7 +177,10 @@ public class FlatMapOpTest extends OpTestCase {
 
         result = exerciseOps(data, s -> LongStream.empty());
         assertEquals(0, result.size());
+    }
 
+    @Test(dataProvider = "LongStreamTestData.small", dataProviderClass = LongStreamTestDataProvider.class)
+    public void testLongOpsX(String name, TestData.OfLong data) {
         exerciseOps(data, s -> s.flatMap(e -> LongStream.range(0, e)));
         exerciseOps(data, s -> s.flatMap(e -> LongStream.range(0, e).limit(10)));
     }
@@ -186,7 +195,10 @@ public class FlatMapOpTest extends OpTestCase {
 
         result = exerciseOps(data, s -> DoubleStream.empty());
         assertEquals(0, result.size());
+    }
 
+    @Test(dataProvider = "DoubleStreamTestData.small", dataProviderClass = DoubleStreamTestDataProvider.class)
+    public void testDoubleOpsX(String name, TestData.OfDouble data) {
         exerciseOps(data, s -> s.flatMap(e -> IntStream.range(0, (int) e).asDoubleStream()));
         exerciseOps(data, s -> s.flatMap(e -> IntStream.range(0, (int) e).limit(10).asDoubleStream()));
     }
diff --git a/jdk/test/javax/net/ssl/templates/SSLSocketSSLEngineTemplate.java b/jdk/test/javax/net/ssl/templates/SSLSocketSSLEngineTemplate.java
index 274f9d8e255..d7f0d38cdee 100644
--- a/jdk/test/javax/net/ssl/templates/SSLSocketSSLEngineTemplate.java
+++ b/jdk/test/javax/net/ssl/templates/SSLSocketSSLEngineTemplate.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -31,6 +31,7 @@
  * @bug 7105780
  * @summary Add SSLSocket client/SSLEngine server to templates directory.
  * @run main/othervm SSLSocketSSLEngineTemplate
+ * @key intermittent
  */
 
 /**
diff --git a/jdk/test/lib/testlibrary/java/util/jar/CreateMultiReleaseTestJars.java b/jdk/test/lib/testlibrary/java/util/jar/CreateMultiReleaseTestJars.java
index 299a9ca65db..390c4fa0c69 100644
--- a/jdk/test/lib/testlibrary/java/util/jar/CreateMultiReleaseTestJars.java
+++ b/jdk/test/lib/testlibrary/java/util/jar/CreateMultiReleaseTestJars.java
@@ -21,12 +21,21 @@
  * questions.
  */
 
+import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.cert.CertPath;
+import java.security.cert.CertificateFactory;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.zip.ZipFile;
+import jdk.security.jarsigner.JarSigner;
 
 public class CreateMultiReleaseTestJars {
     final private String main =
@@ -120,14 +129,21 @@ public class CreateMultiReleaseTestJars {
         String testsrc = System.getProperty("test.src",".");
         String testdir = findTestDir(testsrc);
         String keystore = testdir + "/sun/security/tools/jarsigner/JarSigning.keystore";
-        String[] jsArgs = {
-                "-keystore", keystore,
-                "-storepass", "bbbbbb",
-                "-signedJar", "signed-multi-release.jar",
-                "multi-release.jar", "b"
-        };
-        sun.security.tools.jarsigner.Main.main(jsArgs);
 
+        // jarsigner -keystore keystore -storepass "bbbbbb"
+        //           -signedJar signed-multi-release.jar multi-release.jar b
+
+        char[] password = "bbbbbb".toCharArray();
+        KeyStore ks = KeyStore.getInstance(new File(keystore), password);
+        PrivateKey pkb = (PrivateKey)ks.getKey("b", password);
+        CertPath cp = CertificateFactory.getInstance("X.509")
+                .generateCertPath(Arrays.asList(ks.getCertificateChain("b")));
+        JarSigner js = new JarSigner.Builder(pkb, cp).build();
+        try (ZipFile in = new ZipFile("multi-release.jar");
+             FileOutputStream os = new FileOutputStream("signed-multi-release.jar"))
+        {
+            js.sign(in, os);
+        }
     }
 
     String findTestDir(String dir) throws IOException {
diff --git a/jdk/test/sun/nio/ch/TestMaxCachedBufferSize.java b/jdk/test/sun/nio/ch/TestMaxCachedBufferSize.java
new file mode 100644
index 00000000000..8c8049bdf5c
--- /dev/null
+++ b/jdk/test/sun/nio/ch/TestMaxCachedBufferSize.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.IOException;
+
+import java.lang.management.BufferPoolMXBean;
+import java.lang.management.ManagementFactory;
+
+import java.nio.ByteBuffer;
+
+import java.nio.channels.FileChannel;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import static java.nio.file.StandardOpenOption.CREATE;
+import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
+import static java.nio.file.StandardOpenOption.WRITE;
+
+import java.util.List;
+import java.util.Random;
+
+/*
+ * @test
+ * @requires sun.arch.data.model == "64"
+ * @build TestMaxCachedBufferSize
+ * @run main/othervm TestMaxCachedBufferSize
+ * @run main/othervm -Djdk.nio.maxCachedBufferSize=0 TestMaxCachedBufferSize
+ * @run main/othervm -Djdk.nio.maxCachedBufferSize=2000 TestMaxCachedBufferSize
+ * @run main/othervm -Djdk.nio.maxCachedBufferSize=100000 TestMaxCachedBufferSize
+ * @run main/othervm -Djdk.nio.maxCachedBufferSize=10000000 TestMaxCachedBufferSize
+ * @summary Test the implementation of the jdk.nio.maxCachedBufferSize property.
+ */
+public class TestMaxCachedBufferSize {
+    private static final int DEFAULT_ITERS = 10 * 1000;
+    private static final int DEFAULT_THREAD_NUM = 4;
+
+    private static final int SMALL_BUFFER_MIN_SIZE =  4 * 1024;
+    private static final int SMALL_BUFFER_MAX_SIZE = 64 * 1024;
+    private static final int SMALL_BUFFER_DIFF_SIZE =
+                                 SMALL_BUFFER_MAX_SIZE - SMALL_BUFFER_MIN_SIZE;
+
+    private static final int LARGE_BUFFER_MIN_SIZE =      512 * 1024;
+    private static final int LARGE_BUFFER_MAX_SIZE = 4 * 1024 * 1024;
+    private static final int LARGE_BUFFER_DIFF_SIZE =
+                                 LARGE_BUFFER_MAX_SIZE - LARGE_BUFFER_MIN_SIZE;
+
+    private static final int LARGE_BUFFER_FREQUENCY = 100;
+
+    private static final String FILE_NAME_PREFIX = "nio-out-file-";
+    private static final int VERBOSE_PERIOD = 5 * 1000;
+
+    private static int iters = DEFAULT_ITERS;
+    private static int threadNum = DEFAULT_THREAD_NUM;
+
+    private static BufferPoolMXBean getDirectPool() {
+        final List<BufferPoolMXBean> pools =
+                  ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class);
+        for (BufferPoolMXBean pool : pools) {
+            if (pool.getName().equals("direct")) {
+                return pool;
+            }
+        }
+        throw new Error("could not find direct pool");
+    }
+    private static final BufferPoolMXBean directPool = getDirectPool();
+
+    // Each worker will do write operations on a file channel using
+    // buffers of various sizes. The buffer size is randomly chosen to
+    // be within a small or a large range. This way we can control
+    // which buffers can be cached (all, only the small ones, or none)
+    // by setting the jdk.nio.maxCachedBufferSize property.
+    private static class Worker implements Runnable {
+        private final int id;
+        private final Random random = new Random();
+        private long smallBufferCount = 0;
+        private long largeBufferCount = 0;
+
+        private int getWriteSize() {
+            int minSize = 0;
+            int diff = 0;
+            if (random.nextInt() % LARGE_BUFFER_FREQUENCY != 0) {
+                // small buffer
+                minSize = SMALL_BUFFER_MIN_SIZE;
+                diff = SMALL_BUFFER_DIFF_SIZE;
+                smallBufferCount += 1;
+            } else {
+                // large buffer
+                minSize = LARGE_BUFFER_MIN_SIZE;
+                diff = LARGE_BUFFER_DIFF_SIZE;
+                largeBufferCount += 1;
+            }
+            return minSize + random.nextInt(diff);
+        }
+
+        private void loop() {
+            final String fileName = String.format("%s%d", FILE_NAME_PREFIX, id);
+
+            try {
+                for (int i = 0; i < iters; i += 1) {
+                    final int writeSize = getWriteSize();
+
+                    // This will allocate a HeapByteBuffer. It should not
+                    // be a direct buffer, otherwise the write() method on
+                    // the channel below will not create a temporary
+                    // direct buffer for the write.
+                    final ByteBuffer buffer = ByteBuffer.allocate(writeSize);
+
+                    // Put some random data on it.
+                    while (buffer.hasRemaining()) {
+                        buffer.put((byte) random.nextInt());
+                    }
+                    buffer.rewind();
+
+                    final Path file = Paths.get(fileName);
+                    try (FileChannel outChannel = FileChannel.open(file, CREATE, TRUNCATE_EXISTING, WRITE)) {
+                        // The write() method will create a temporary
+                        // direct buffer for the write and attempt to cache
+                        // it. It's important that buffer is not a
+                        // direct buffer, otherwise the temporary buffer
+                        // will not be created.
+                        long res = outChannel.write(buffer);
+                    }
+
+                    if ((i + 1) % VERBOSE_PERIOD == 0) {
+                        System.out.printf(
+                          " Worker %3d | %8d Iters | Small %8d Large %8d | Direct %4d / %7dK\n",
+                          id, i + 1, smallBufferCount, largeBufferCount,
+                          directPool.getCount(), directPool.getTotalCapacity() / 1024);
+                    }
+                }
+            } catch (IOException e) {
+                throw new Error("I/O error", e);
+            }
+        }
+
+        @Override
+        public void run() {
+            loop();
+        }
+
+        public Worker(int id) {
+            this.id = id;
+        }
+    }
+
+    public static void checkDirectBuffers(long expectedCount, long expectedMax) {
+        final long directCount = directPool.getCount();
+        final long directTotalCapacity = directPool.getTotalCapacity();
+        System.out.printf("Direct %d / %dK\n",
+                          directCount, directTotalCapacity / 1024);
+
+        // Note that directCount could be < expectedCount. This can
+        // happen if a GC occurs after one of the worker threads exits
+        // since its thread-local DirectByteBuffer could be cleaned up
+        // before we reach here.
+        if (directCount > expectedCount) {
+            throw new Error(String.format(
+                "inconsistent direct buffer total count, expected = %d, found = %d",
+                expectedCount, directCount));
+        }
+
+        if (directTotalCapacity > expectedMax) {
+            throw new Error(String.format(
+                "inconsistent direct buffer total capacity, expectex max = %d, found = %d",
+                expectedMax, directTotalCapacity));
+        }
+    }
+
+    public static void main(String[] args) {
+        final String maxBufferSizeStr = System.getProperty("jdk.nio.maxCachedBufferSize");
+        final long maxBufferSize =
+            (maxBufferSizeStr != null) ? Long.valueOf(maxBufferSizeStr) : Long.MAX_VALUE;
+
+        // We assume that the max cannot be equal to a size of a
+        // buffer that can be allocated (makes sanity checking at the
+        // end easier).
+        if ((SMALL_BUFFER_MIN_SIZE <= maxBufferSize &&
+                                     maxBufferSize <= SMALL_BUFFER_MAX_SIZE) ||
+            (LARGE_BUFFER_MIN_SIZE <= maxBufferSize &&
+                                     maxBufferSize <= LARGE_BUFFER_MAX_SIZE)) {
+            throw new Error(String.format("max buffer size = %d not allowed",
+                                          maxBufferSize));
+        }
+
+        System.out.printf("Threads %d | Iterations %d | MaxBufferSize %d\n",
+                          threadNum, iters, maxBufferSize);
+        System.out.println();
+
+        final Thread[] threads = new Thread[threadNum];
+        for (int i = 0; i < threadNum; i += 1) {
+            threads[i] = new Thread(new Worker(i));
+            threads[i].start();
+        }
+
+        try {
+            for (int i = 0; i < threadNum; i += 1) {
+                threads[i].join();
+            }
+        } catch (InterruptedException e) {
+            throw new Error("join() interrupted!", e);
+        }
+
+        // There is an assumption here that, at this point, only the
+        // cached DirectByteBuffers should be active. Given we
+        // haven't used any other DirectByteBuffers in this test, this
+        // should hold.
+        //
+        // Also note that we can only do the sanity checking at the
+        // end and not during the run given that, at any time, there
+        // could be buffers currently in use by some of the workers
+        // that will not be cached.
+
+        System.out.println();
+        if (maxBufferSize < SMALL_BUFFER_MAX_SIZE) {
+            // The max buffer size is smaller than all buffers that
+            // were allocated. No buffers should have been cached.
+            checkDirectBuffers(0, 0);
+        } else if (maxBufferSize < LARGE_BUFFER_MIN_SIZE) {
+            // The max buffer size is larger than all small buffers
+            // but smaller than all large buffers that were
+            // allocated. Only small buffers could have been cached.
+            checkDirectBuffers(threadNum,
+                               (long) threadNum * (long) SMALL_BUFFER_MAX_SIZE);
+        } else {
+            // The max buffer size is larger than all buffers that
+            // were allocated. All buffers could have been cached.
+            checkDirectBuffers(threadNum,
+                               (long) threadNum * (long) LARGE_BUFFER_MAX_SIZE);
+        }
+    }
+}
diff --git a/jdk/test/sun/security/pkcs11/Cipher/ReinitCipher.java b/jdk/test/sun/security/pkcs11/Cipher/ReinitCipher.java
index 2cd68b44a9b..db7098e5fdf 100644
--- a/jdk/test/sun/security/pkcs11/Cipher/ReinitCipher.java
+++ b/jdk/test/sun/security/pkcs11/Cipher/ReinitCipher.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2016, 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
@@ -28,21 +28,22 @@
  * @author Andreas Sterbenz
  * @library ..
  * @key randomness
+ * @run main/othervm ReinitCipher
+ * @run main/othervm ReinitCipher sm
  */
 
-import java.util.*;
-
-import java.security.*;
-
-import javax.crypto.*;
-import javax.crypto.spec.*;
+import java.security.Provider;
+import java.util.Random;
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
 
 public class ReinitCipher extends PKCS11Test {
 
     public static void main(String[] args) throws Exception {
-        main(new ReinitCipher());
+        main(new ReinitCipher(), args);
     }
 
+    @Override
     public void main(Provider p) throws Exception {
         if (p.getService("Cipher", "ARCFOUR") == null) {
             System.out.println("Not supported by provider, skipping");
diff --git a/jdk/test/sun/security/pkcs11/Cipher/TestPKCS5PaddingError.java b/jdk/test/sun/security/pkcs11/Cipher/TestPKCS5PaddingError.java
index f0721daa2e5..5fa94cbfc54 100644
--- a/jdk/test/sun/security/pkcs11/Cipher/TestPKCS5PaddingError.java
+++ b/jdk/test/sun/security/pkcs11/Cipher/TestPKCS5PaddingError.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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,16 +27,18 @@
  * @summary Test internal PKCS5Padding impl with various error conditions.
  * @author Valerie Peng
  * @library ..
+ * @run main/othervm TestPKCS5PaddingError
+ * @run main/othervm TestPKCS5PaddingError sm
  */
-import java.io.*;
-import java.nio.*;
-import java.util.*;
 
-import java.security.*;
-import java.security.spec.AlgorithmParameterSpec;
-
-import javax.crypto.*;
-import javax.crypto.spec.IvParameterSpec;
+import java.security.AlgorithmParameters;
+import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
 
 public class TestPKCS5PaddingError extends PKCS11Test {
     private static class CI { // class for holding Cipher Information
@@ -62,10 +64,8 @@ public class TestPKCS5PaddingError extends PKCS11Test {
 
     private static StringBuffer debugBuf = new StringBuffer();
 
+    @Override
     public void main(Provider p) throws Exception {
-        boolean status = true;
-        Random random = new Random();
-
         try {
             byte[] plainText = new byte[200];
 
@@ -127,6 +127,6 @@ public class TestPKCS5PaddingError extends PKCS11Test {
     }
 
     public static void main(String[] args) throws Exception {
-        main(new TestPKCS5PaddingError());
+        main(new TestPKCS5PaddingError(), args);
     }
 }
diff --git a/jdk/test/sun/security/pkcs11/Cipher/TestRSACipher.java b/jdk/test/sun/security/pkcs11/Cipher/TestRSACipher.java
index 876048f46cf..b9656a54086 100644
--- a/jdk/test/sun/security/pkcs11/Cipher/TestRSACipher.java
+++ b/jdk/test/sun/security/pkcs11/Cipher/TestRSACipher.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2016, 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
@@ -28,20 +28,28 @@
  * @author Andreas Sterbenz
  * @library ..
  * @key randomness
+ * @run main/othervm TestRSACipher
+ * @run main/othervm TestRSACipher sm
  */
 
-import java.io.*;
-import java.util.*;
-
-import java.security.*;
-
-import javax.crypto.*;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.util.Arrays;
+import java.util.Random;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
 
 public class TestRSACipher extends PKCS11Test {
 
     private static final String[] RSA_ALGOS =
         { "RSA/ECB/PKCS1Padding", "RSA" };
 
+    @Override
     public void main(Provider p) throws Exception {
         try {
             Cipher.getInstance(RSA_ALGOS[0], p);
@@ -122,7 +130,7 @@ public class TestRSACipher extends PKCS11Test {
     }
 
     public static void main(String[] args) throws Exception {
-        main(new TestRSACipher());
+        main(new TestRSACipher(), args);
     }
 
 }
diff --git a/jdk/test/sun/security/pkcs11/Cipher/TestRSACipherWrap.java b/jdk/test/sun/security/pkcs11/Cipher/TestRSACipherWrap.java
index c00b39942cc..8637302b547 100644
--- a/jdk/test/sun/security/pkcs11/Cipher/TestRSACipherWrap.java
+++ b/jdk/test/sun/security/pkcs11/Cipher/TestRSACipherWrap.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2016, 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,13 +27,20 @@
  * @summary basic test for RSA cipher key wrapping functionality
  * @author Valerie Peng
  * @library ..
+ * @run main/othervm TestRSACipherWrap
+ * @run main/othervm TestRSACipherWrap sm
  */
-import java.io.*;
-import java.util.*;
 
-import java.security.*;
-
-import javax.crypto.*;
+import java.security.GeneralSecurityException;
+import java.security.InvalidParameterException;
+import java.security.Key;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.Provider;
+import java.util.Arrays;
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
 import javax.crypto.spec.SecretKeySpec;
 
 public class TestRSACipherWrap extends PKCS11Test {
@@ -41,6 +48,7 @@ public class TestRSACipherWrap extends PKCS11Test {
     private static final String[] RSA_ALGOS =
         { "RSA/ECB/PKCS1Padding", "RSA" };
 
+    @Override
     public void main(Provider p) throws Exception {
         try {
             Cipher.getInstance(RSA_ALGOS[0], p);
@@ -104,6 +112,6 @@ public class TestRSACipherWrap extends PKCS11Test {
     }
 
     public static void main(String[] args) throws Exception {
-        main(new TestRSACipherWrap());
+        main(new TestRSACipherWrap(), args);
     }
 }
diff --git a/jdk/test/sun/security/pkcs11/Cipher/TestRawRSACipher.java b/jdk/test/sun/security/pkcs11/Cipher/TestRawRSACipher.java
index 17ffe9553c2..cebe4224129 100644
--- a/jdk/test/sun/security/pkcs11/Cipher/TestRawRSACipher.java
+++ b/jdk/test/sun/security/pkcs11/Cipher/TestRawRSACipher.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2016, 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
@@ -28,16 +28,21 @@
  * @author Valerie Peng
  * @library ..
  * @key randomness
+ * @run main/othervm TestRawRSACipher
+ * @run main/othervm TestRawRSACipher sm
  */
 
-import javax.crypto.*;
-import java.io.*;
-import javax.crypto.spec.SecretKeySpec;
-import java.security.*;
-import java.util.*;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.Provider;
+import java.util.Arrays;
+import java.util.Random;
+import javax.crypto.Cipher;
 
 public class TestRawRSACipher extends PKCS11Test {
 
+    @Override
     public void main(Provider p) throws Exception {
         try {
             Cipher.getInstance("RSA/ECB/NoPadding", p);
@@ -80,6 +85,6 @@ public class TestRawRSACipher extends PKCS11Test {
     }
 
     public static void main(String[] args) throws Exception {
-        main(new TestRawRSACipher());
+        main(new TestRawRSACipher(), args);
     }
 }
diff --git a/jdk/test/sun/security/pkcs11/Cipher/TestSymmCiphers.java b/jdk/test/sun/security/pkcs11/Cipher/TestSymmCiphers.java
index e6b7f7d8a34..018edc7417a 100644
--- a/jdk/test/sun/security/pkcs11/Cipher/TestSymmCiphers.java
+++ b/jdk/test/sun/security/pkcs11/Cipher/TestSymmCiphers.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2016, 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
@@ -28,16 +28,19 @@
  * @author Valerie Peng
  * @library ..
  * @key randomness
+ * @run main/othervm TestSymmCiphers
+ * @run main/othervm TestSymmCiphers sm
  */
-import java.io.*;
-import java.nio.*;
-import java.util.*;
 
-import java.security.*;
-import java.security.spec.AlgorithmParameterSpec;
-
-import javax.crypto.*;
-import javax.crypto.spec.IvParameterSpec;
+import java.io.ByteArrayOutputStream;
+import java.nio.ByteBuffer;
+import java.security.AlgorithmParameters;
+import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+import java.util.Random;
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
 
 public class TestSymmCiphers extends PKCS11Test {
 
@@ -81,6 +84,7 @@ public class TestSymmCiphers extends PKCS11Test {
     };
     private static StringBuffer debugBuf = new StringBuffer();
 
+    @Override
     public void main(Provider p) throws Exception {
         // NSS reports CKR_DEVICE_ERROR when the data passed to
         // its EncryptUpdate/DecryptUpdate is not multiple of blocks
@@ -272,6 +276,6 @@ public class TestSymmCiphers extends PKCS11Test {
     }
 
     public static void main(String[] args) throws Exception {
-        main(new TestSymmCiphers());
+        main(new TestSymmCiphers(), args);
     }
 }
diff --git a/jdk/test/sun/security/pkcs11/Cipher/TestSymmCiphersNoPad.java b/jdk/test/sun/security/pkcs11/Cipher/TestSymmCiphersNoPad.java
index 07d48649be5..2359e77bf03 100644
--- a/jdk/test/sun/security/pkcs11/Cipher/TestSymmCiphersNoPad.java
+++ b/jdk/test/sun/security/pkcs11/Cipher/TestSymmCiphersNoPad.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2016, 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
@@ -28,17 +28,22 @@
  * @author Valerie Peng
  * @library ..
  * @key randomness
+ * @run main/othervm TestSymmCiphersNoPad
+ * @run main/othervm TestSymmCiphersNoPad sm
  */
 
-import java.io.*;
-import java.nio.*;
-import java.util.*;
-
-import java.security.*;
-import java.security.spec.AlgorithmParameterSpec;
-
-import javax.crypto.*;
-import javax.crypto.spec.IvParameterSpec;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.security.AlgorithmParameters;
+import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+import java.util.Random;
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
 
 public class TestSymmCiphersNoPad extends PKCS11Test {
 
@@ -67,6 +72,7 @@ public class TestSymmCiphersNoPad extends PKCS11Test {
 
     private static StringBuffer debugBuf;
 
+    @Override
     public void main(Provider p) throws Exception {
         boolean status = true;
         Random random = new Random();
@@ -234,6 +240,6 @@ public class TestSymmCiphersNoPad extends PKCS11Test {
     }
 
     public static void main(String[] args) throws Exception {
-        main(new TestSymmCiphersNoPad());
+        main(new TestSymmCiphersNoPad(), args);
     }
 }
diff --git a/jdk/test/sun/security/pkcs11/KeyAgreement/TestDH.java b/jdk/test/sun/security/pkcs11/KeyAgreement/TestDH.java
index 2c123a84a06..45e03897359 100644
--- a/jdk/test/sun/security/pkcs11/KeyAgreement/TestDH.java
+++ b/jdk/test/sun/security/pkcs11/KeyAgreement/TestDH.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2016, 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,17 +27,20 @@
  * @summary Verify that DH works properly
  * @author Andreas Sterbenz
  * @library ..
+ * @run main/othervm TestDH
+ * @run main/othervm TestDH sm
  */
 
-import java.io.*;
-import java.util.*;
-
-import java.security.*;
-
-import javax.crypto.*;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.Provider;
+import java.util.Arrays;
+import javax.crypto.KeyAgreement;
+import javax.crypto.SecretKey;
 
 public class TestDH extends PKCS11Test {
 
+    @Override
     public void main(Provider p) throws Exception {
         if (p.getService("KeyAgreement", "DH") == null) {
             System.out.println("DH not supported, skipping");
@@ -91,8 +94,9 @@ public class TestDH extends PKCS11Test {
         testAlgorithm(ka2, kp2, ka1, kp1, "TlsPremasterSecret");
     }
 
-    private static void testAlgorithm(KeyAgreement ka1, KeyPair kp1, KeyAgreement ka2, KeyPair kp2, String algorithm) throws Exception {
-        SecretKey key1 = null;
+    private static void testAlgorithm(KeyAgreement ka1, KeyPair kp1,
+            KeyAgreement ka2, KeyPair kp2, String algorithm) throws Exception {
+        SecretKey key1;
 
         ka1.init(kp1.getPrivate());
         ka1.doPhase(kp2.getPublic(), true);
@@ -115,7 +119,7 @@ public class TestDH extends PKCS11Test {
     }
 
     public static void main(String[] args) throws Exception {
-        main(new TestDH());
+        main(new TestDH(), args);
     }
 
 }
diff --git a/jdk/test/sun/security/pkcs11/KeyAgreement/TestInterop.java b/jdk/test/sun/security/pkcs11/KeyAgreement/TestInterop.java
index 95f1ce1c52f..15a96b9b19e 100644
--- a/jdk/test/sun/security/pkcs11/KeyAgreement/TestInterop.java
+++ b/jdk/test/sun/security/pkcs11/KeyAgreement/TestInterop.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2016, 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
@@ -26,14 +26,18 @@
  * @bug 7146728
  * @summary Interop test for DH with secret that has a leading 0x00 byte
  * @library ..
+ * @run main/othervm TestInterop
+ * @run main/othervm TestInterop sm
  */
 import java.math.BigInteger;
-import java.util.*;
-
-import java.security.*;
-
-import javax.crypto.*;
-import javax.crypto.spec.*;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.util.Arrays;
+import javax.crypto.KeyAgreement;
+import javax.crypto.spec.DHPrivateKeySpec;
+import javax.crypto.spec.DHPublicKeySpec;
 
 public class TestInterop extends PKCS11Test {
 
@@ -72,6 +76,7 @@ public class TestInterop extends PKCS11Test {
     + "30313414180008978013330410484011186019824874948204261839391153650949864"
     + "429505597086564709");
 
+    @Override
     public void main(Provider prov) throws Exception {
         if (prov.getService("KeyAgreement", "DH") == null) {
             System.out.println("DH not supported, skipping");
@@ -138,6 +143,6 @@ public class TestInterop extends PKCS11Test {
     }
 
     public static void main(String[] args) throws Exception {
-        main(new TestInterop());
+        main(new TestInterop(), args);
     }
 }
diff --git a/jdk/test/sun/security/pkcs11/KeyAgreement/TestShort.java b/jdk/test/sun/security/pkcs11/KeyAgreement/TestShort.java
index ee9332764c8..aec6c0c7c16 100644
--- a/jdk/test/sun/security/pkcs11/KeyAgreement/TestShort.java
+++ b/jdk/test/sun/security/pkcs11/KeyAgreement/TestShort.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2016, 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,15 +27,19 @@
  * @summary KAT test for DH (normal and with secret that has leading a 0x00 byte)
  * @author Andreas Sterbenz
  * @library ..
+ * @run main/othervm TestShort
+ * @run main/othervm TestShort sm
  */
 
 import java.math.BigInteger;
-import java.util.*;
-
-import java.security.*;
-
-import javax.crypto.*;
-import javax.crypto.spec.*;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.util.Arrays;
+import javax.crypto.KeyAgreement;
+import javax.crypto.spec.DHPrivateKeySpec;
+import javax.crypto.spec.DHPublicKeySpec;
 
 public class TestShort extends PKCS11Test {
 
@@ -83,6 +87,7 @@ public class TestShort extends PKCS11Test {
     + "1a:6a:15:d8:a4:8c:0a:ce:f0:15:03:0c:c2:56:82:a2:75:9b:49:fe:ed:60:c5:6e"
     + ":de:47:55:62:4f:16:20:6d:74:cc:7b:95:93:25:2c:ea");
 
+    @Override
     public void main(Provider provider) throws Exception {
         if (provider.getService("KeyAgreement", "DH") == null) {
             System.out.println("DH not supported, skipping");
@@ -142,7 +147,7 @@ public class TestShort extends PKCS11Test {
     }
 
     public static void main(String[] args) throws Exception {
-        main(new TestShort());
+        main(new TestShort(), args);
     }
 
 }
diff --git a/jdk/test/sun/security/pkcs11/KeyGenerator/DESParity.java b/jdk/test/sun/security/pkcs11/KeyGenerator/DESParity.java
index 2d7f77bc9c5..3c8b3a52283 100644
--- a/jdk/test/sun/security/pkcs11/KeyGenerator/DESParity.java
+++ b/jdk/test/sun/security/pkcs11/KeyGenerator/DESParity.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2016, 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
@@ -28,19 +28,21 @@
  * @author Andreas Sterbenz
  * @library ..
  * @key randomness
+ * @run main/othervm DESParity
+ * @run main/othervm DESParity sm
  */
 
-import java.io.*;
-import java.util.*;
-
-import java.security.*;
-import java.security.spec.*;
-
-import javax.crypto.*;
-import javax.crypto.spec.*;
+import java.security.Provider;
+import java.util.Random;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.DESKeySpec;
+import javax.crypto.spec.DESedeKeySpec;
+import javax.crypto.spec.SecretKeySpec;
 
 public class DESParity extends PKCS11Test {
 
+    @Override
     public void main(Provider p) throws Exception {
         if (p.getService("SecretKeyFactory", "DES") == null) {
             System.out.println("Not supported by provider, skipping");
@@ -73,7 +75,7 @@ public class DESParity extends PKCS11Test {
     }
 
     public static void main(String[] args) throws Exception {
-        main(new DESParity());
+        main(new DESParity(), args);
     }
 
 }
diff --git a/jdk/test/sun/security/pkcs11/KeyGenerator/TestKeyGenerator.java b/jdk/test/sun/security/pkcs11/KeyGenerator/TestKeyGenerator.java
index 3bd7e96f117..e0f669f58e3 100644
--- a/jdk/test/sun/security/pkcs11/KeyGenerator/TestKeyGenerator.java
+++ b/jdk/test/sun/security/pkcs11/KeyGenerator/TestKeyGenerator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2016, 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,13 +27,16 @@
  * @summary test the KeyGenerator
  * @author Andreas Sterbenz
  * @library ..
+ * @run main/othervm TestKeyGenerator
+ * @run main/othervm TestKeyGenerator sm
  */
 
-import java.util.*;
-
-import java.security.*;
-
-import javax.crypto.*;
+import java.security.InvalidParameterException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+import java.security.ProviderException;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
 
 enum TestResult {
     PASS,
@@ -44,7 +47,7 @@ enum TestResult {
 public class TestKeyGenerator extends PKCS11Test {
 
     public static void main(String[] args) throws Exception {
-        main(new TestKeyGenerator());
+        main(new TestKeyGenerator(), args);
     }
 
     private TestResult test(String algorithm, int keyLen, Provider p,
@@ -85,6 +88,7 @@ public class TestKeyGenerator extends PKCS11Test {
         return actual;
     }
 
+    @Override
     public void main(Provider p) throws Exception {
         test("DES", 0, p, TestResult.FAIL);
         test("DES", 56, p, TestResult.PASS); // ensure JCE-Compatibility
diff --git a/jdk/test/sun/security/pkcs11/KeyPairGenerator/TestDH2048.java b/jdk/test/sun/security/pkcs11/KeyPairGenerator/TestDH2048.java
index c11911a12fd..840e1ce3dcf 100644
--- a/jdk/test/sun/security/pkcs11/KeyPairGenerator/TestDH2048.java
+++ b/jdk/test/sun/security/pkcs11/KeyPairGenerator/TestDH2048.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2016, 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,14 +27,14 @@
  * @summary Ensure that 2048-bit DH key pairs can be generated
  * @author Valerie Peng
  * @library ..
+ * @run main/othervm TestDH2048
+ * @run main/othervm TestDH2048 sm
  */
 
-import java.io.*;
-import java.util.*;
-
-import java.security.*;
-
-import javax.crypto.*;
+import java.security.InvalidParameterException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.Provider;
 
 public class TestDH2048 extends PKCS11Test {
 
@@ -47,6 +47,7 @@ public class TestDH2048 extends PKCS11Test {
         }
     }
 
+    @Override
     public void main(Provider p) throws Exception {
         if (p.getService("KeyPairGenerator", "DH") == null) {
             System.out.println("KPG for DH not supported, skipping");
@@ -61,6 +62,6 @@ public class TestDH2048 extends PKCS11Test {
     }
 
     public static void main(String[] args) throws Exception {
-        main(new TestDH2048());
+        main(new TestDH2048(), args);
     }
 }
diff --git a/jdk/test/sun/security/pkcs11/Mac/MacKAT.java b/jdk/test/sun/security/pkcs11/Mac/MacKAT.java
index c7da9297159..d7679f91ffd 100644
--- a/jdk/test/sun/security/pkcs11/Mac/MacKAT.java
+++ b/jdk/test/sun/security/pkcs11/Mac/MacKAT.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -36,7 +36,8 @@ import javax.crypto.spec.SecretKeySpec;
  * @summary Basic known-answer-test for Hmac algorithms
  * @author Andreas Sterbenz
  * @library ..
- * @run main MacKAT
+ * @run main/othervm MacKAT
+ * @run main/othervm MacKAT sm
  */
 public class MacKAT extends PKCS11Test {
 
@@ -178,7 +179,7 @@ public class MacKAT extends PKCS11Test {
     };
 
     public static void main(String[] args) throws Exception {
-        main(new MacKAT());
+        main(new MacKAT(), args);
     }
 
     @Override
diff --git a/jdk/test/sun/security/pkcs11/Mac/MacSameTest.java b/jdk/test/sun/security/pkcs11/Mac/MacSameTest.java
index 21eae39b0c6..8d6689721fc 100644
--- a/jdk/test/sun/security/pkcs11/Mac/MacSameTest.java
+++ b/jdk/test/sun/security/pkcs11/Mac/MacSameTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -36,7 +36,8 @@ import javax.crypto.spec.SecretKeySpec;
  * @summary Check if doFinal and update operation result in same Mac
  * @author Yu-Ching Valerie Peng, Bill Situ, Alexander Fomin
  * @library ..
- * @run main MacSameTest
+ * @run main/othervm MacSameTest
+ * @run main/othervm MacSameTest sm
  * @key randomness
  */
 public class MacSameTest extends PKCS11Test {
@@ -57,7 +58,7 @@ public class MacSameTest extends PKCS11Test {
      * @param args the command line arguments
      */
     public static void main(String[] args) throws Exception {
-        main(new MacSameTest());
+        main(new MacSameTest(), args);
     }
 
     @Override
diff --git a/jdk/test/sun/security/pkcs11/Mac/ReinitMac.java b/jdk/test/sun/security/pkcs11/Mac/ReinitMac.java
index 68bef343889..39970d8df00 100644
--- a/jdk/test/sun/security/pkcs11/Mac/ReinitMac.java
+++ b/jdk/test/sun/security/pkcs11/Mac/ReinitMac.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2016, 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
@@ -28,21 +28,22 @@
  * @author Andreas Sterbenz
  * @library ..
  * @key randomness
+ * @run main/othervm ReinitMac
+ * @run main/othervm ReinitMac sm
  */
 
-import java.util.*;
-
-import java.security.*;
-
-import javax.crypto.*;
-import javax.crypto.spec.*;
+import java.security.Provider;
+import java.util.Random;
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
 
 public class ReinitMac extends PKCS11Test {
 
     public static void main(String[] args) throws Exception {
-        main(new ReinitMac());
+        main(new ReinitMac(), args);
     }
 
+    @Override
     public void main(Provider p) throws Exception {
         if (p.getService("Mac", "HmacMD5") == null) {
             System.out.println(p + " does not support HmacMD5, skipping");
diff --git a/jdk/test/sun/security/pkcs11/MessageDigest/ByteBuffers.java b/jdk/test/sun/security/pkcs11/MessageDigest/ByteBuffers.java
index 1b502a95bcf..eca0485c8a9 100644
--- a/jdk/test/sun/security/pkcs11/MessageDigest/ByteBuffers.java
+++ b/jdk/test/sun/security/pkcs11/MessageDigest/ByteBuffers.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2016, 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
@@ -28,19 +28,23 @@
  * @author Andreas Sterbenz
  * @library ..
  * @key randomness
+ * @run main/othervm ByteBuffers
+ * @run main/othervm ByteBuffers sm
  */
 
-import java.util.*;
-import java.nio.*;
-
-import java.security.*;
+import java.nio.ByteBuffer;
+import java.security.MessageDigest;
+import java.security.Provider;
+import java.util.Arrays;
+import java.util.Random;
 
 public class ByteBuffers extends PKCS11Test {
 
     public static void main(String[] args) throws Exception {
-        main(new ByteBuffers());
+        main(new ByteBuffers(), args);
     }
 
+    @Override
     public void main(Provider p) throws Exception {
         if (p.getService("MessageDigest", "MD5") == null) {
             System.out.println("Provider does not support MD5, skipping");
diff --git a/jdk/test/sun/security/pkcs11/MessageDigest/DigestKAT.java b/jdk/test/sun/security/pkcs11/MessageDigest/DigestKAT.java
index e6f948d064f..0f48f28c859 100644
--- a/jdk/test/sun/security/pkcs11/MessageDigest/DigestKAT.java
+++ b/jdk/test/sun/security/pkcs11/MessageDigest/DigestKAT.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2016, 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,19 +27,23 @@
  * @summary Basic known-answer-test for all our MessageDigest algorithms
  * @author Andreas Sterbenz
  * @library ..
+ * @run main/othervm DigestKAT
+ * @run main/othervm DigestKAT sm
  */
 
-import java.io.*;
-import java.util.*;
-
-import java.security.*;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.StringReader;
+import java.security.MessageDigest;
+import java.security.Provider;
+import java.util.Arrays;
 
 public class DigestKAT extends PKCS11Test {
 
     private final static char[] hexDigits = "0123456789abcdef".toCharArray();
 
     public static String toString(byte[] b) {
-        StringBuffer sb = new StringBuffer(b.length * 3);
+        StringBuilder sb = new StringBuilder(b.length * 3);
         for (int i = 0; i < b.length; i++) {
             int k = b[i] & 0xff;
             if (i != 0) {
@@ -106,6 +110,7 @@ public class DigestKAT extends PKCS11Test {
             this.data = data;
             this.digest = digest;
         }
+        @Override
         void run(Provider p) throws Exception {
             if (p.getService("MessageDigest", alg) == null) {
                 System.out.println("Skipped " + alg);
@@ -123,7 +128,6 @@ public class DigestKAT extends PKCS11Test {
                 System.out.println("out:  " + DigestKAT.toString(myDigest));
                 throw new Exception("Digest test for " + alg + " failed");
             }
-//          System.out.println("Passed " + alg);
         }
     }
 
@@ -221,12 +225,13 @@ public class DigestKAT extends PKCS11Test {
         System.out.println("Done (" + (stop - start) + " ms).");
     }
 
+    @Override
     public void main(Provider p) throws Exception{
         runTests(tests, p);
     }
 
     public static void main(String[] args) throws Exception {
-        main(new DigestKAT());
+        main(new DigestKAT(), args);
     }
 
 }
diff --git a/jdk/test/sun/security/pkcs11/MessageDigest/ReinitDigest.java b/jdk/test/sun/security/pkcs11/MessageDigest/ReinitDigest.java
index 2f8290a2a7a..9d8250c9ab0 100644
--- a/jdk/test/sun/security/pkcs11/MessageDigest/ReinitDigest.java
+++ b/jdk/test/sun/security/pkcs11/MessageDigest/ReinitDigest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2016, 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
@@ -28,18 +28,22 @@
  * @author Andreas Sterbenz
  * @library ..
  * @key randomness
+ * @run main/othervm ReinitDigest
+ * @run main/othervm ReinitDigest sm
  */
 
-import java.util.*;
-
-import java.security.*;
+import java.security.MessageDigest;
+import java.security.Provider;
+import java.util.Arrays;
+import java.util.Random;
 
 public class ReinitDigest extends PKCS11Test {
 
     public static void main(String[] args) throws Exception {
-        main(new ReinitDigest());
+        main(new ReinitDigest(), args);
     }
 
+    @Override
     public void main(Provider p) throws Exception {
         if (p.getService("MessageDigest", "MD5") == null) {
             System.out.println("Provider does not support MD5, skipping");
diff --git a/jdk/test/sun/security/pkcs11/MessageDigest/TestCloning.java b/jdk/test/sun/security/pkcs11/MessageDigest/TestCloning.java
index 5ea2264109f..0aca8fbf791 100644
--- a/jdk/test/sun/security/pkcs11/MessageDigest/TestCloning.java
+++ b/jdk/test/sun/security/pkcs11/MessageDigest/TestCloning.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2016, 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
@@ -28,11 +28,14 @@
  * @author Valerie Peng
  * @library ..
  * @key randomness
+ * @run main/othervm TestCloning
+ * @run main/othervm TestCloning sm
  */
 
-import java.util.*;
-
-import java.security.*;
+import java.security.MessageDigest;
+import java.security.Provider;
+import java.util.Arrays;
+import java.util.Random;
 
 public class TestCloning extends PKCS11Test {
 
@@ -41,13 +44,14 @@ public class TestCloning extends PKCS11Test {
     };
 
     public static void main(String[] args) throws Exception {
-        main(new TestCloning());
+        main(new TestCloning(), args);
     }
 
     private static final byte[] data1 = new byte[10];
     private static final byte[] data2 = new byte[10*1024];
 
 
+    @Override
     public void main(Provider p) throws Exception {
         Random r = new Random();
         byte[] data1 = new byte[10];
diff --git a/jdk/test/sun/security/pkcs11/PKCS11Test.java b/jdk/test/sun/security/pkcs11/PKCS11Test.java
index e0a17eaa87e..46621e1ae2e 100644
--- a/jdk/test/sun/security/pkcs11/PKCS11Test.java
+++ b/jdk/test/sun/security/pkcs11/PKCS11Test.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2016, 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,15 +24,38 @@
 
 // common infrastructure for SunPKCS11 tests
 
-import java.io.*;
-import java.util.*;
-
-import java.security.*;
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.StringReader;
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.security.ProviderException;
+import java.security.Security;
 import java.security.spec.ECGenParameterSpec;
 import java.security.spec.ECParameterSpec;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.ServiceLoader;
+import java.util.Set;
 
 public abstract class PKCS11Test {
 
+    private boolean enableSM = false;
+
+    static final Properties props = System.getProperties();
+
     static final String PKCS11 = "PKCS11";
 
     // directory of the test source
@@ -40,7 +63,8 @@ public abstract class PKCS11Test {
 
     static final char SEP = File.separatorChar;
 
-    private final static String REL_CLOSED = "../../../../closed/sun/security/pkcs11".replace('/', SEP);
+    private static final String DEFAULT_POLICY =
+            BASE + SEP + ".." + SEP + "policy";
 
     // directory corresponding to BASE in the /closed hierarchy
     static final String CLOSED_BASE;
@@ -53,6 +77,9 @@ public abstract class PKCS11Test {
         String p1 = absBase.substring(0, k + 6);
         String p2 = absBase.substring(k + 5);
         CLOSED_BASE = p1 + "closed" + p2;
+
+        // set it as a system property to make it available in policy file
+        System.setProperty("closed.base", CLOSED_BASE);
     }
 
     static String NSPR_PREFIX = "";
@@ -86,7 +113,7 @@ public abstract class PKCS11Test {
                 if (p.getName().equals("SunPKCS11")) {
                     found = true;
                     break;
-                };
+                }
             } catch (Exception e) {
                 // ignore and move on to the next one
             }
@@ -103,6 +130,19 @@ public abstract class PKCS11Test {
         pkcs11 = p;
     }
 
+    /*
+     * Use Solaris SPARC 11.2 or later to avoid an intermittent failure
+     * when running SunPKCS11-Solaris (8044554)
+     */
+    static boolean isBadSolarisSparc(Provider p) {
+        if ("SunPKCS11-Solaris".equals(p.getName()) && badSolarisSparc) {
+            System.out.println("SunPKCS11-Solaris provider requires " +
+                "Solaris SPARC 11.2 or later, skipping");
+            return true;
+        }
+        return false;
+    }
+
     // Return a SunPKCS11 provider configured with the specified config file
     static Provider getSunPKCS11(String config) throws Exception {
         if (pkcs11 == null) {
@@ -114,15 +154,43 @@ public abstract class PKCS11Test {
     public abstract void main(Provider p) throws Exception;
 
     private void premain(Provider p) throws Exception {
-        long start = System.currentTimeMillis();
-        System.out.println("Running test with provider " + p.getName() + "...");
-        main(p);
-        long stop = System.currentTimeMillis();
-        System.out.println("Completed test with provider " + p.getName() +
-            " (" + (stop - start) + " ms).");
+        // set a security manager and policy before a test case runs,
+        // and disable them after the test case finished
+        try {
+            if (enableSM) {
+                System.setSecurityManager(new SecurityManager());
+            }
+            long start = System.currentTimeMillis();
+            System.out.printf(
+                    "Running test with provider %s (security manager %s) ...%n",
+                        p.getName(), enableSM ? "enabled" : "disabled");
+            main(p);
+            long stop = System.currentTimeMillis();
+            System.out.println("Completed test with provider " + p.getName() +
+                " (" + (stop - start) + " ms).");
+        } finally {
+            if (enableSM) {
+                System.setSecurityManager(null);
+            }
+        }
     }
 
     public static void main(PKCS11Test test) throws Exception {
+        main(test, null);
+    }
+
+    public static void main(PKCS11Test test, String[] args) throws Exception {
+        if (args != null) {
+            if (args.length > 0 && "sm".equals(args[0])) {
+                test.enableSM = true;
+            }
+            if (test.enableSM) {
+                System.setProperty("java.security.policy",
+                        (args.length > 1) ? BASE + SEP + args[1]
+                                : DEFAULT_POLICY);
+            }
+        }
+
         Provider[] oldProviders = Security.getProviders();
         try {
             System.out.println("Beginning test run " + test.getClass().getName() + "...");
@@ -218,7 +286,6 @@ public abstract class PKCS11Test {
     }
 
     static String getNSSLibDir(String library) throws Exception {
-        Properties props = System.getProperties();
         String osName = props.getProperty("os.name");
         if (osName.startsWith("Win")) {
             osName = "Windows";
@@ -249,6 +316,15 @@ public abstract class PKCS11Test {
         return nssLibDir;
     }
 
+    static boolean isBadNSSVersion(Provider p) {
+        if (isNSS(p) && badNSSVersion) {
+            System.out.println("NSS 3.11 has a DER issue that recent " +
+                    "version do not.");
+            return true;
+        }
+        return false;
+    }
+
     protected static void safeReload(String lib) throws Exception {
         try {
             System.load(lib);
@@ -317,34 +393,32 @@ public abstract class PKCS11Test {
 
         try {
             libfile = getNSSLibDir() + System.mapLibraryName(library);
-            FileInputStream is = new FileInputStream(libfile);
-            byte[] data = new byte[1000];
-            int read = 0;
+            try (FileInputStream is = new FileInputStream(libfile)) {
+                byte[] data = new byte[1000];
+                int read = 0;
 
-            while (is.available() > 0) {
-                if (read == 0) {
-                    read = is.read(data, 0, 1000);
-                } else {
-                    // Prepend last 100 bytes in case the header was split
-                    // between the reads.
-                    System.arraycopy(data, 900, data, 0, 100);
-                    read = 100 + is.read(data, 100, 900);
-                }
+                while (is.available() > 0) {
+                    if (read == 0) {
+                        read = is.read(data, 0, 1000);
+                    } else {
+                        // Prepend last 100 bytes in case the header was split
+                        // between the reads.
+                        System.arraycopy(data, 900, data, 0, 100);
+                        read = 100 + is.read(data, 100, 900);
+                    }
 
-                s = new String(data, 0, read);
-                if ((i = s.indexOf(nssHeader)) > 0) {
-                    found = true;
-                    // If the nssHeader is before 920 we can break, otherwise
-                    // we may not have the whole header so do another read.  If
-                    // no bytes are in the stream, that is ok, found is true.
-                    if (i < 920) {
-                        break;
+                    s = new String(data, 0, read);
+                    if ((i = s.indexOf(nssHeader)) > 0) {
+                        found = true;
+                        // If the nssHeader is before 920 we can break, otherwise
+                        // we may not have the whole header so do another read.  If
+                        // no bytes are in the stream, that is ok, found is true.
+                        if (i < 920) {
+                            break;
+                        }
                     }
                 }
             }
-
-            is.close();
-
         } catch (Exception e) {
             e.printStackTrace();
         }
@@ -438,14 +512,13 @@ public abstract class PKCS11Test {
     }
 
     // Generate a vector of supported elliptic curves of a given provider
-    static Vector<ECParameterSpec> getKnownCurves(Provider p) throws Exception {
+    static List<ECParameterSpec> getKnownCurves(Provider p) throws Exception {
         int index;
         int begin;
         int end;
         String curve;
-        KeyPair kp = null;
 
-        Vector<ECParameterSpec> results = new Vector<ECParameterSpec>();
+        List<ECParameterSpec> results = new ArrayList<>();
         // Get Curves to test from SunEC.
         String kcProp = Security.getProvider("SunEC").
                 getProperty("AlgorithmParameters.EC SupportedCurves");
@@ -483,7 +556,7 @@ public abstract class PKCS11Test {
             try {
                 KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", p);
                 kpg.initialize(e);
-                kp = kpg.generateKeyPair();
+                kpg.generateKeyPair();
                 results.add(e);
                 System.out.println("Supported");
             } catch (ProviderException ex) {
@@ -514,9 +587,8 @@ public abstract class PKCS11Test {
     }
 
     // Check support for a curve with a provided Vector of EC support
-    boolean checkSupport(Vector<ECParameterSpec> supportedEC,
+    boolean checkSupport(List<ECParameterSpec> supportedEC,
             ECParameterSpec curve) {
-        boolean found = false;
         for (ECParameterSpec ec: supportedEC) {
             if (ec.equals(curve)) {
                 return true;
@@ -529,7 +601,7 @@ public abstract class PKCS11Test {
 
     // Location of the NSS libraries on each supported platform
     static {
-        osMap = new HashMap<String,String[]>();
+        osMap = new HashMap<>();
         osMap.put("SunOS-sparc-32", new String[]{"/usr/lib/mps/"});
         osMap.put("SunOS-sparcv9-64", new String[]{"/usr/lib/mps/64/"});
         osMap.put("SunOS-x86-32", new String[]{"/usr/lib/mps/"});
@@ -551,11 +623,20 @@ public abstract class PKCS11Test {
 
     private final static char[] hexDigits = "0123456789abcdef".toCharArray();
 
+    static final boolean badNSSVersion =
+            getNSSVersion() >= 3.11 && getNSSVersion() < 3.12;
+
+    static final boolean badSolarisSparc =
+            System.getProperty("os.name").equals("SunOS") &&
+            System.getProperty("os.arch").equals("sparcv9") &&
+            System.getProperty("os.version").compareTo("5.11") <= 0 &&
+            getDistro().compareTo("11.2") < 0;
+
     public static String toString(byte[] b) {
         if (b == null) {
             return "(null)";
         }
-        StringBuffer sb = new StringBuffer(b.length * 3);
+        StringBuilder sb = new StringBuilder(b.length * 3);
         for (int i = 0; i < b.length; i++) {
             int k = b[i] & 0xff;
             if (i != 0) {
@@ -637,8 +718,7 @@ public abstract class PKCS11Test {
     /**
      * Get the identifier for the operating system distribution
      */
-    public String getDistro() {
-
+    static String getDistro() {
         try (BufferedReader in =
             new BufferedReader(new InputStreamReader(
                 Runtime.getRuntime().exec("uname -v").getInputStream()))) {
diff --git a/jdk/test/sun/security/pkcs11/Secmod/AddPrivateKey.java b/jdk/test/sun/security/pkcs11/Secmod/AddPrivateKey.java
index 56833bd2cc6..67c691a3bc2 100644
--- a/jdk/test/sun/security/pkcs11/Secmod/AddPrivateKey.java
+++ b/jdk/test/sun/security/pkcs11/Secmod/AddPrivateKey.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2016, 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
@@ -28,14 +28,26 @@
  * @author Andreas Sterbenz
  * @library ..
  * @run main/othervm AddPrivateKey
+ * @run main/othervm AddPrivateKey sm policy
  */
 
-import java.io.*;
-import java.util.*;
-
-import java.security.*;
-import java.security.KeyStore.*;
-import java.security.cert.*;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.security.KeyFactory;
+import java.security.KeyStore;
+import java.security.KeyStore.PasswordProtection;
+import java.security.KeyStore.PrivateKeyEntry;
+import java.security.KeyStoreException;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.Security;
+import java.security.Signature;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 
 // this test is currently only run for the NSS KeyStore provider, but it
 // is really a generic KeyStore test so it should be modified to run for
@@ -63,6 +75,12 @@ public class AddPrivateKey extends SecmodTest {
         System.out.println();
         Security.addProvider(p);
 
+        if (args.length > 1 && "sm".equals(args[0])) {
+            System.setProperty("java.security.policy",
+                    BASE + File.separator + args[1]);
+            System.setSecurityManager(new SecurityManager());
+        }
+
         KeyStore ks = KeyStore.getInstance(PKCS11, p);
         ks.load(null, password);
         for (String alias : aliases(ks)) {
diff --git a/jdk/test/sun/security/pkcs11/Secmod/AddTrustedCert.java b/jdk/test/sun/security/pkcs11/Secmod/AddTrustedCert.java
index 6b2b545611a..2eedf3cd506 100644
--- a/jdk/test/sun/security/pkcs11/Secmod/AddTrustedCert.java
+++ b/jdk/test/sun/security/pkcs11/Secmod/AddTrustedCert.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2016, 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
@@ -28,14 +28,21 @@
  * @author Andreas Sterbenz
  * @library ..
  * @run main/othervm AddTrustedCert
+ * @run main/othervm AddTrustedCert sm policy
  */
 
-import java.io.*;
-import java.util.*;
-
-import java.security.*;
-import java.security.KeyStore.*;
-import java.security.cert.*;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.security.KeyStore;
+import java.security.KeyStore.TrustedCertificateEntry;
+import java.security.Provider;
+import java.security.Security;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.TreeSet;
 
 public class AddTrustedCert extends SecmodTest {
 
@@ -56,6 +63,13 @@ public class AddTrustedCert extends SecmodTest {
 
         System.out.println(p);
         Security.addProvider(p);
+
+        if (args.length > 1 && "sm".equals(args[0])) {
+            System.setProperty("java.security.policy",
+                    BASE + File.separator + args[1]);
+            System.setSecurityManager(new SecurityManager());
+        }
+
         KeyStore ks = KeyStore.getInstance(PKCS11, p);
         ks.load(null, password);
         Collection<String> aliases = new TreeSet<>(Collections.list(
diff --git a/jdk/test/sun/security/pkcs11/Secmod/Crypto.java b/jdk/test/sun/security/pkcs11/Secmod/Crypto.java
index 735149f1c84..2e571051cbd 100644
--- a/jdk/test/sun/security/pkcs11/Secmod/Crypto.java
+++ b/jdk/test/sun/security/pkcs11/Secmod/Crypto.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2016, 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
@@ -28,9 +28,14 @@
  * @author Andreas Sterbenz
  * @library ..
  * @run main/othervm Crypto
+ * @run main/othervm Crypto sm policy
  */
 
-import java.security.*;
+import java.io.File;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.Provider;
+import java.security.Signature;
 
 public class Crypto extends SecmodTest {
 
@@ -42,6 +47,12 @@ public class Crypto extends SecmodTest {
         String configName = BASE + SEP + "nsscrypto.cfg";
         Provider p = getSunPKCS11(configName);
 
+        if (args.length > 1 && "sm".equals(args[0])) {
+            System.setProperty("java.security.policy",
+                    BASE + File.separator + args[1]);
+            System.setSecurityManager(new SecurityManager());
+        }
+
         KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", p);
         KeyPair kp = kpg.generateKeyPair();
 
diff --git a/jdk/test/sun/security/pkcs11/Secmod/GetPrivateKey.java b/jdk/test/sun/security/pkcs11/Secmod/GetPrivateKey.java
index a2547652348..b3170597b5d 100644
--- a/jdk/test/sun/security/pkcs11/Secmod/GetPrivateKey.java
+++ b/jdk/test/sun/security/pkcs11/Secmod/GetPrivateKey.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2016, 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
@@ -29,13 +29,19 @@
  * @author Andreas Sterbenz
  * @library ..
  * @run main/othervm GetPrivateKey
+ * @run main/othervm GetPrivateKey sm policy
  */
 
-import java.util.*;
-
-import java.security.*;
-import java.security.KeyStore.*;
-import java.security.cert.*;
+import java.io.File;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.Security;
+import java.security.Signature;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.TreeSet;
 
 public class GetPrivateKey extends SecmodTest {
 
@@ -49,6 +55,13 @@ public class GetPrivateKey extends SecmodTest {
 
         System.out.println(p);
         Security.addProvider(p);
+
+        if (args.length > 1 && "sm".equals(args[0])) {
+            System.setProperty("java.security.policy",
+                    BASE + File.separator + args[1]);
+            System.setSecurityManager(new SecurityManager());
+        }
+
         KeyStore ks = KeyStore.getInstance(PKCS11, p);
         ks.load(null, password);
         Collection<String> aliases = new TreeSet<>(
diff --git a/jdk/test/sun/security/pkcs11/Secmod/JksSetPrivateKey.java b/jdk/test/sun/security/pkcs11/Secmod/JksSetPrivateKey.java
index e3a60befff4..7613fce9ba5 100644
--- a/jdk/test/sun/security/pkcs11/Secmod/JksSetPrivateKey.java
+++ b/jdk/test/sun/security/pkcs11/Secmod/JksSetPrivateKey.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2016, 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
@@ -28,13 +28,19 @@
  * @author Wang Weijun
  * @library ..
  * @run main/othervm JksSetPrivateKey
+ * @run main/othervm JksSetPrivateKey sm policy
  */
 
-import java.util.*;
-
-import java.security.*;
-import java.security.KeyStore.*;
-import java.security.cert.*;
+import java.io.File;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.Security;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.TreeSet;
 
 public class JksSetPrivateKey extends SecmodTest {
 
@@ -48,9 +54,16 @@ public class JksSetPrivateKey extends SecmodTest {
 
         System.out.println(p);
         Security.addProvider(p);
+
+        if (args.length > 1 && "sm".equals(args[0])) {
+            System.setProperty("java.security.policy",
+                    BASE + File.separator + args[1]);
+            System.setSecurityManager(new SecurityManager());
+        }
+
         KeyStore ks = KeyStore.getInstance("PKCS11", p);
         ks.load(null, password);
-        Collection<String> aliases = new TreeSet<String>(Collections.list(ks.aliases()));
+        Collection<String> aliases = new TreeSet<>(Collections.list(ks.aliases()));
         System.out.println("entries: " + aliases.size());
         System.out.println(aliases);
 
@@ -66,14 +79,14 @@ public class JksSetPrivateKey extends SecmodTest {
             jks.setKeyEntry("k1", privateKey, "changeit".toCharArray(), chain);
             throw new Exception("No, an NSS PrivateKey shouldn't be extractable and put inside a JKS keystore");
         } catch (KeyStoreException e) {
-            System.err.println(e);; // This is OK
+            System.err.println(e); // This is OK
         }
 
         try {
             jks.setKeyEntry("k2", new DummyPrivateKey(), "changeit".toCharArray(), chain);
             throw new Exception("No, non-PKCS#8 key shouldn't be put inside a KeyStore");
         } catch (KeyStoreException e) {
-            System.err.println(e);; // This is OK
+            System.err.println(e); // This is OK
         }
         System.out.println("OK");
 
@@ -81,35 +94,41 @@ public class JksSetPrivateKey extends SecmodTest {
             jks.setKeyEntry("k3", new DummyPrivateKey2(), "changeit".toCharArray(), chain);
             throw new Exception("No, not-extractble key shouldn't be put inside a KeyStore");
         } catch (KeyStoreException e) {
-            System.err.println(e);; // This is OK
+            System.err.println(e); // This is OK
         }
         System.out.println("OK");
     }
 }
 
 class DummyPrivateKey implements PrivateKey {
+    @Override
     public String getAlgorithm() {
         return "DUMMY";
     }
 
+    @Override
     public String getFormat() {
         return "DUMMY";
     }
 
+    @Override
     public byte[] getEncoded() {
         return "DUMMY".getBytes();
     }
 }
 
 class DummyPrivateKey2 implements PrivateKey {
+    @Override
     public String getAlgorithm() {
         return "DUMMY";
     }
 
+    @Override
     public String getFormat() {
         return "PKCS#8";
     }
 
+    @Override
     public byte[] getEncoded() {
         return null;
     }
diff --git a/jdk/test/sun/security/pkcs11/Secmod/LoadKeystore.java b/jdk/test/sun/security/pkcs11/Secmod/LoadKeystore.java
index fd1aa694ddf..c08fe8446aa 100644
--- a/jdk/test/sun/security/pkcs11/Secmod/LoadKeystore.java
+++ b/jdk/test/sun/security/pkcs11/Secmod/LoadKeystore.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, 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
@@ -21,6 +21,7 @@
  * questions.
  */
 
+import java.io.File;
 import java.io.IOException;
 import java.security.KeyStore;
 import java.security.KeyStoreException;
@@ -35,6 +36,7 @@ import java.util.Collections;
  * @summary Checks that PKCS#11 keystore can't be loaded with wrong password
  * @library ../
  * @run main/othervm LoadKeystore
+ * @run main/othervm LoadKeystore sm policy
  */
 public class LoadKeystore extends SecmodTest {
 
@@ -50,6 +52,12 @@ public class LoadKeystore extends SecmodTest {
         System.out.println();
         Security.addProvider(p);
 
+        if (args.length > 1 && "sm".equals(args[0])) {
+            System.setProperty("java.security.policy",
+                    BASE + File.separator + args[1]);
+            System.setSecurityManager(new SecurityManager());
+        }
+
         try {
             System.out.println("Load keystore with wrong type");
             KeyStore.getInstance("unknown", p);
diff --git a/jdk/test/sun/security/pkcs11/Secmod/TrustAnchors.java b/jdk/test/sun/security/pkcs11/Secmod/TrustAnchors.java
index 3d4a43bff30..277a51ec4c2 100644
--- a/jdk/test/sun/security/pkcs11/Secmod/TrustAnchors.java
+++ b/jdk/test/sun/security/pkcs11/Secmod/TrustAnchors.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2016, 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
@@ -28,13 +28,17 @@
  * @author Andreas Sterbenz
  * @library ..
  * @run main/othervm TrustAnchors
+ * @run main/othervm TrustAnchors sm policy
  */
 
-import java.util.*;
-
-import java.security.*;
-import java.security.KeyStore.*;
-import java.security.cert.*;
+import java.io.File;
+import java.security.KeyStore;
+import java.security.Provider;
+import java.security.Security;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.TreeSet;
 
 public class TrustAnchors extends SecmodTest {
 
@@ -57,9 +61,16 @@ public class TrustAnchors extends SecmodTest {
 
         System.out.println(p);
         Security.addProvider(p);
+
+        if (args.length > 1 && "sm".equals(args[0])) {
+            System.setProperty("java.security.policy",
+                    BASE + File.separator + args[1]);
+            System.setSecurityManager(new SecurityManager());
+        }
+
         KeyStore ks = KeyStore.getInstance("PKCS11", p);
         ks.load(null, null);
-        Collection<String> aliases = new TreeSet<String>(Collections.list(ks.aliases()));
+        Collection<String> aliases = new TreeSet<>(Collections.list(ks.aliases()));
         System.out.println("entries: " + aliases.size());
         System.out.println(aliases);
 
diff --git a/jdk/test/sun/security/pkcs11/Secmod/policy b/jdk/test/sun/security/pkcs11/Secmod/policy
new file mode 100644
index 00000000000..e4c95ca6dd5
--- /dev/null
+++ b/jdk/test/sun/security/pkcs11/Secmod/policy
@@ -0,0 +1,6 @@
+grant {
+    permission java.security.SecurityPermission "authProvider.*";
+    permission java.io.FilePermission "${test.src}/-", "read";
+    permission java.io.FilePermission "${pkcs11test.nss.db}/-", "read";
+    permission java.io.FilePermission "${pkcs11test.nss.libdir}/-", "read";
+};
\ No newline at end of file
diff --git a/jdk/test/sun/security/pkcs11/SecureRandom/Basic.java b/jdk/test/sun/security/pkcs11/SecureRandom/Basic.java
index f9bfb1759ef..8c2c1686f73 100644
--- a/jdk/test/sun/security/pkcs11/SecureRandom/Basic.java
+++ b/jdk/test/sun/security/pkcs11/SecureRandom/Basic.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2016, 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
@@ -28,17 +28,17 @@
  * @author Andreas Sterbenz
  * @library ..
  * @key randomness
+ * @run main/othervm Basic
+ * @run main/othervm Basic sm
  */
 
-import java.io.*;
-import java.util.*;
-
-import java.security.*;
-
-import javax.crypto.*;
+import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+import java.security.SecureRandom;
 
 public class Basic extends PKCS11Test {
 
+    @Override
     public void main(Provider p) throws Exception {
         SecureRandom random;
         try {
@@ -58,7 +58,7 @@ public class Basic extends PKCS11Test {
     }
 
     public static void main(String[] args) throws Exception {
-        main(new Basic());
+        main(new Basic(), args);
     }
 
 }
diff --git a/jdk/test/sun/security/pkcs11/Signature/ByteBuffers.java b/jdk/test/sun/security/pkcs11/Signature/ByteBuffers.java
index d45063a8eda..39e4424af35 100644
--- a/jdk/test/sun/security/pkcs11/Signature/ByteBuffers.java
+++ b/jdk/test/sun/security/pkcs11/Signature/ByteBuffers.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2016, 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
@@ -28,19 +28,24 @@
  * @author Andreas Sterbenz
  * @library ..
  * @key randomness
+ * @run main/othervm ByteBuffers
+ * @run main/othervm ByteBuffers sm
  */
 
-import java.util.*;
-import java.nio.*;
-
-import java.security.*;
+import java.nio.ByteBuffer;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.Provider;
+import java.security.Signature;
+import java.util.Random;
 
 public class ByteBuffers extends PKCS11Test {
 
     public static void main(String[] args) throws Exception {
-        main(new ByteBuffers());
+        main(new ByteBuffers(), args);
     }
 
+    @Override
     public void main(Provider p) throws Exception {
 
         /*
@@ -48,9 +53,9 @@ public class ByteBuffers extends PKCS11Test {
          * when running SunPKCS11-Solaris provider (8044554)
          */
         if (p.getName().equals("SunPKCS11-Solaris") &&
-            System.getProperty("os.name").equals("SunOS") &&
-            System.getProperty("os.arch").equals("sparcv9") &&
-            System.getProperty("os.version").compareTo("5.11") <= 0 &&
+            props.getProperty("os.name").equals("SunOS") &&
+            props.getProperty("os.arch").equals("sparcv9") &&
+            props.getProperty("os.version").compareTo("5.11") <= 0 &&
             getDistro().compareTo("11.2") < 0) {
 
             System.out.println("SunPKCS11-Solaris provider requires " +
diff --git a/jdk/test/sun/security/pkcs11/Signature/TestDSA.java b/jdk/test/sun/security/pkcs11/Signature/TestDSA.java
index 2b81bb3a333..c4fcf5d5956 100644
--- a/jdk/test/sun/security/pkcs11/Signature/TestDSA.java
+++ b/jdk/test/sun/security/pkcs11/Signature/TestDSA.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2016, 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
@@ -28,14 +28,24 @@
  * @author Andreas Sterbenz
  * @library ..
  * @key randomness
+ * @run main/othervm TestDSA
+ * @run main/othervm TestDSA sm
  */
 
-import java.io.*;
-import java.util.*;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.StringReader;
 import java.math.BigInteger;
-
-import java.security.*;
-import java.security.spec.*;
+import java.security.KeyFactory;
+import java.security.MessageDigest;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.spec.DSAPrivateKeySpec;
+import java.security.spec.DSAPublicKeySpec;
+import java.util.Random;
 
 public class TestDSA extends PKCS11Test {
 
@@ -102,9 +112,10 @@ public class TestDSA extends PKCS11Test {
     }
 
     public static void main(String[] args) throws Exception {
-        main(new TestDSA());
+        main(new TestDSA(), args);
     }
 
+    @Override
     public void main(Provider provider) throws Exception {
         long start = System.currentTimeMillis();
 
@@ -115,9 +126,9 @@ public class TestDSA extends PKCS11Test {
          * when running SunPKCS11-Solaris (8044554)
          */
         if (provider.getName().equals("SunPKCS11-Solaris") &&
-            System.getProperty("os.name").equals("SunOS") &&
-            System.getProperty("os.arch").equals("sparcv9") &&
-            System.getProperty("os.version").compareTo("5.11") <= 0 &&
+            props.getProperty("os.name").equals("SunOS") &&
+            props.getProperty("os.arch").equals("sparcv9") &&
+            props.getProperty("os.version").compareTo("5.11") <= 0 &&
             getDistro().compareTo("11.2") < 0) {
 
             System.out.println("SunPKCS11-Solaris provider requires " +
diff --git a/jdk/test/sun/security/pkcs11/Signature/TestDSAKeyLength.java b/jdk/test/sun/security/pkcs11/Signature/TestDSAKeyLength.java
index efe97077628..f671bba3bd7 100644
--- a/jdk/test/sun/security/pkcs11/Signature/TestDSAKeyLength.java
+++ b/jdk/test/sun/security/pkcs11/Signature/TestDSAKeyLength.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2016, 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
@@ -20,6 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
+
 /*
  * @test
  * @bug 7200306 8029158
@@ -27,19 +28,24 @@
  * with unsupported key sizes
  * @library ..
  * @key randomness
+ * @run main/othervm TestDSAKeyLength
+ * @run main/othervm TestDSAKeyLength sm
  */
 
-
-import java.security.*;
-import java.security.spec.*;
-import java.security.interfaces.*;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.Provider;
+import java.security.SecureRandom;
+import java.security.Signature;
 
 public class TestDSAKeyLength extends PKCS11Test {
 
     public static void main(String[] args) throws Exception {
-        main(new TestDSAKeyLength());
+        main(new TestDSAKeyLength(), args);
     }
 
+    @Override
     public void main(Provider provider) throws Exception {
         if (isNSS(provider) && getNSSVersion() >= 3.14) {
             System.out.println("Skip testing NSS " + getNSSVersion());
@@ -51,9 +57,9 @@ public class TestDSAKeyLength extends PKCS11Test {
          * when running SunPKCS11-Solaris (8044554)
          */
         if (provider.getName().equals("SunPKCS11-Solaris") &&
-            System.getProperty("os.name").equals("SunOS") &&
-            System.getProperty("os.arch").equals("sparcv9") &&
-            System.getProperty("os.version").compareTo("5.11") <= 0 &&
+            props.getProperty("os.name").equals("SunOS") &&
+            props.getProperty("os.arch").equals("sparcv9") &&
+            props.getProperty("os.version").compareTo("5.11") <= 0 &&
             getDistro().compareTo("11.2") < 0) {
 
             System.out.println("SunPKCS11-Solaris provider requires " +
diff --git a/jdk/test/sun/security/pkcs11/Signature/TestRSAKeyLength.java b/jdk/test/sun/security/pkcs11/Signature/TestRSAKeyLength.java
index 0b79e68d20c..43341472e99 100644
--- a/jdk/test/sun/security/pkcs11/Signature/TestRSAKeyLength.java
+++ b/jdk/test/sun/security/pkcs11/Signature/TestRSAKeyLength.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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,14 +27,26 @@
  * @summary Make sure initSign/initVerify() check RSA key lengths
  * @author Yu-Ching Valerie Peng
  * @library ..
+ * @run main/othervm TestRSAKeyLength
+ * @run main/othervm TestRSAKeyLength sm
  */
 
-import java.security.*;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignedObject;
 
 public class TestRSAKeyLength extends PKCS11Test {
+
     public static void main(String[] args) throws Exception {
-        main(new TestRSAKeyLength());
+        main(new TestRSAKeyLength(), args);
     }
+
+    @Override
     public void main(Provider p) throws Exception {
 
         /*
@@ -42,9 +54,9 @@ public class TestRSAKeyLength extends PKCS11Test {
          * when running SunPKCS11-Solaris (8044554)
          */
         if (p.getName().equals("SunPKCS11-Solaris") &&
-            System.getProperty("os.name").equals("SunOS") &&
-            System.getProperty("os.arch").equals("sparcv9") &&
-            System.getProperty("os.version").compareTo("5.11") <= 0 &&
+            props.getProperty("os.name").equals("SunOS") &&
+            props.getProperty("os.arch").equals("sparcv9") &&
+            props.getProperty("os.version").compareTo("5.11") <= 0 &&
             getDistro().compareTo("11.2") < 0) {
 
             System.out.println("SunPKCS11-Solaris provider requires " +
diff --git a/jdk/test/sun/security/pkcs11/ec/ReadCertificates.java b/jdk/test/sun/security/pkcs11/ec/ReadCertificates.java
index 82b7f4abc26..4c0bbfba8f6 100644
--- a/jdk/test/sun/security/pkcs11/ec/ReadCertificates.java
+++ b/jdk/test/sun/security/pkcs11/ec/ReadCertificates.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2016, 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
@@ -29,16 +29,31 @@
  * @author Andreas Sterbenz
  * @library ..
  * @library ../../../../java/security/testlibrary
+ * @run main/othervm ReadCertificates
+ * @run main/othervm ReadCertificates sm policy
  */
 
-import java.io.*;
-import java.util.*;
-
-import java.security.cert.*;
-import java.security.*;
-import java.security.interfaces.*;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.SignatureException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.ECPublicKey;
 import java.security.spec.ECParameterSpec;
-
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
 import javax.security.auth.x500.X500Principal;
 
 public class ReadCertificates extends PKCS11Test {
@@ -49,16 +64,18 @@ public class ReadCertificates extends PKCS11Test {
 
     private static Collection<X509Certificate> readCertificates(File file) throws Exception {
         System.out.println("Loading " + file.getName() + "...");
-        InputStream in = new FileInputStream(file);
-        Collection<X509Certificate> certs = (Collection<X509Certificate>)factory.generateCertificates(in);
-        in.close();
+        Collection<X509Certificate> certs;
+        try (InputStream in = new FileInputStream(file)) {
+            certs = (Collection<X509Certificate>)factory.generateCertificates(in);
+        }
         return certs;
     }
 
     public static void main(String[] args) throws Exception {
-        main(new ReadCertificates());
+        main(new ReadCertificates(), args);
     }
 
+    @Override
     public void main(Provider p) throws Exception {
         if (p.getService("Signature", "SHA1withECDSA") == null) {
             System.out.println("Provider does not support ECDSA, skipping...");
@@ -79,7 +96,7 @@ public class ReadCertificates extends PKCS11Test {
         } catch (CertificateException e) {
             // ignore
         }
-        Map<X500Principal,X509Certificate> certs = new LinkedHashMap<X500Principal,X509Certificate>();
+        Map<X500Principal,X509Certificate> certs = new LinkedHashMap<>();
 
         File dir = new File(BASE, "certs");
         File closedDir = new File(CLOSED_BASE, "certs");
@@ -103,7 +120,7 @@ public class ReadCertificates extends PKCS11Test {
         System.out.println("OK: " + certs.size() + " certificates.");
 
         // Get supported curves
-        Vector<ECParameterSpec> supportedEC = getKnownCurves(p);
+        List<ECParameterSpec> supportedEC = getKnownCurves(p);
 
         System.out.println("Test Certs:\n");
         for (X509Certificate cert : certs.values()) {
@@ -127,7 +144,8 @@ public class ReadCertificates extends PKCS11Test {
                System.out.println("Warning: " + e.getMessage() +
                    ". Trying another provider...");
                cert.verify(key);
-           } catch (Exception e) {
+           } catch (CertificateException | InvalidKeyException |
+                    NoSuchProviderException | SignatureException e) {
                System.out.println(e.getMessage());
                if (key instanceof ECPublicKey) {
                    System.out.println("Failed.\n\tCurve: " +
@@ -145,7 +163,7 @@ public class ReadCertificates extends PKCS11Test {
         // try some random invalid signatures to make sure we get the correct
         // error
         System.out.println("Checking incorrect signatures...");
-        List<X509Certificate> certList = new ArrayList<X509Certificate>(certs.values());
+        List<X509Certificate> certList = new ArrayList<>(certs.values());
         for (int i = 0; i < 20; i++) {
             X509Certificate cert, signer;
             do {
@@ -161,9 +179,7 @@ public class ReadCertificates extends PKCS11Test {
                 } else {
                     throw new Exception("Verified invalid signature");
                 }
-            } catch (SignatureException e) {
-                System.out.println("OK: " + e);
-            } catch (InvalidKeyException e) {
+            } catch (SignatureException | InvalidKeyException e) {
                 System.out.println("OK: " + e);
             }
         }
diff --git a/jdk/test/sun/security/pkcs11/ec/ReadPKCS12.java b/jdk/test/sun/security/pkcs11/ec/ReadPKCS12.java
index 112470509fe..3252b08d195 100644
--- a/jdk/test/sun/security/pkcs11/ec/ReadPKCS12.java
+++ b/jdk/test/sun/security/pkcs11/ec/ReadPKCS12.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2016, 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
@@ -29,26 +29,41 @@
  * @library ..
  * @library ../../../../java/security/testlibrary
  * @key randomness
+ * @run main/othervm ReadPKCS12
+ * @run main/othervm ReadPKCS12 sm policy
  */
 
-import java.io.*;
-import java.util.*;
-
-import java.security.*;
-import java.security.interfaces.*;
-import java.security.cert.*;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.Signature;
 import java.security.cert.Certificate;
-
-import javax.security.auth.x500.X500Principal;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
 
 public class ReadPKCS12 extends PKCS11Test {
 
     private final static boolean COPY = false;
 
     public static void main(String[] args) throws Exception {
-        main(new ReadPKCS12());
+        main(new ReadPKCS12(), args);
     }
 
+    @Override
     public void main(Provider p) throws Exception {
         if (p.getService("Signature", "SHA1withECDSA") == null) {
             System.out.println("Provider does not support ECDSA, skipping...");
@@ -71,29 +86,30 @@ public class ReadPKCS12 extends PKCS11Test {
         KeyStore ks2;
         if (COPY) {
             ks2 = KeyStore.getInstance("JKS");
-            InputStream in = new FileInputStream("keystore.old");
-            ks2.load(in, "passphrase".toCharArray());
-            in.close();
+            try (InputStream in = new FileInputStream("keystore.old")) {
+                ks2.load(in, "passphrase".toCharArray());
+            }
         }
 
         File dir = new File(BASE, "pkcs12");
         File closedDir = new File(CLOSED_BASE, "pkcs12");
 
-        Map<String,char[]> passwords = new HashMap<String,char[]>();
-        BufferedReader reader = new BufferedReader(new FileReader((new File(BASE, "p12passwords.txt"))));
-        while (true) {
-            String line = reader.readLine();
-            if (line == null) {
-                break;
+        Map<String,char[]> passwords = new HashMap<>();
+        try (BufferedReader reader = new BufferedReader(
+                new FileReader(new File(BASE, "p12passwords.txt")))) {
+            while (true) {
+                String line = reader.readLine();
+                if (line == null) {
+                    break;
+                }
+                line = line.trim();
+                if ((line.length() == 0) || line.startsWith("#")) {
+                    continue;
+                }
+                String[] s = line.split(" ");
+                passwords.put(s[0], s[1].toCharArray());
             }
-            line = line.trim();
-            if ((line.length() == 0) || line.startsWith("#")) {
-                continue;
-            }
-            String[] s = line.split(" ");
-            passwords.put(s[0], s[1].toCharArray());
         }
-        reader.close();
 
         for (File file : concat(dir.listFiles(), closedDir.listFiles())) {
             String name = file.getName();
@@ -108,10 +124,11 @@ public class ReadPKCS12 extends PKCS11Test {
                 password = passwords.get("*");
             }
 
-            InputStream in = new FileInputStream(file);
-            KeyStore ks = KeyStore.getInstance("PKCS12");
-            ks.load(in, password);
-            in.close();
+            KeyStore ks;
+            try (InputStream in = new FileInputStream(file)) {
+                ks = KeyStore.getInstance("PKCS12");
+                ks.load(in, password);
+            }
             List<String> aliases = Collections.list(ks.aliases());
             System.out.println("Aliases: " + aliases);
 
@@ -147,9 +164,9 @@ public class ReadPKCS12 extends PKCS11Test {
         }
 
         if (COPY) {
-            OutputStream out = new FileOutputStream("keystore.new");
-            ks2.store(out, "passphrase".toCharArray());
-            out.close();
+            try (OutputStream out = new FileOutputStream("keystore.new")) {
+                ks2.store(out, "passphrase".toCharArray());
+            }
         }
 
         System.out.println("OK");
diff --git a/jdk/test/sun/security/pkcs11/ec/TestCurves.java b/jdk/test/sun/security/pkcs11/ec/TestCurves.java
index 3ada7788cf9..d8e52a9aa69 100644
--- a/jdk/test/sun/security/pkcs11/ec/TestCurves.java
+++ b/jdk/test/sun/security/pkcs11/ec/TestCurves.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2016, 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
@@ -29,53 +29,46 @@
  * @library ..
  * @modules jdk.crypto.pkcs11/sun.security.pkcs11.wrapper
  * @compile -XDignore.symbol.file TestCurves.java
- * @run main TestCurves
+ * @run main/othervm TestCurves
+ * @run main/othervm TestCurves sm
  * @key randomness
  */
 
-import java.util.*;
-
-import java.security.*;
-import java.security.spec.*;
-
-import javax.crypto.*;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.Provider;
+import java.security.ProviderException;
+import java.security.Signature;
+import java.security.spec.ECParameterSpec;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+import javax.crypto.KeyAgreement;
 
 public class TestCurves extends PKCS11Test {
 
     public static void main(String[] args) throws Exception {
-        main(new TestCurves());
+        main(new TestCurves(), args);
     }
 
+    @Override
     public void main(Provider p) throws Exception {
         if (p.getService("KeyAgreement", "ECDH") == null) {
             System.out.println("Not supported by provider, skipping");
             return;
         }
 
-        if (isNSS(p) && getNSSVersion() >= 3.11 && getNSSVersion() < 3.12) {
-            System.out.println("NSS 3.11 has a DER issue that recent " +
-                    "version do not.");
+        if (isBadNSSVersion(p)) {
             return;
         }
 
-        /*
-         * Use Solaris SPARC 11.2 or later to avoid an intermittent failure
-         * when running SunPKCS11-Solaris (8044554)
-         */
-        if (p.getName().equals("SunPKCS11-Solaris") &&
-            System.getProperty("os.name").equals("SunOS") &&
-            System.getProperty("os.arch").equals("sparcv9") &&
-            System.getProperty("os.version").compareTo("5.11") <= 0 &&
-            getDistro().compareTo("11.2") < 0) {
-
-            System.out.println("SunPKCS11-Solaris provider requires " +
-                "Solaris SPARC 11.2 or later, skipping");
+        if (isBadSolarisSparc(p)) {
             return;
         }
 
         // Check if this is sparc for later failure avoidance.
         boolean sparc = false;
-        if (System.getProperty("os.arch").equals("sparcv9")) {
+        if (props.getProperty("os.arch").equals("sparcv9")) {
             sparc = true;
             System.out.println("This is a sparcv9");
         }
@@ -84,7 +77,7 @@ public class TestCurves extends PKCS11Test {
         byte[] data = new byte[2048];
         random.nextBytes(data);
 
-        Vector<ECParameterSpec> curves = getKnownCurves(p);
+        List<ECParameterSpec> curves = getKnownCurves(p);
         for (ECParameterSpec params : curves) {
             System.out.println("Testing " + params + "...");
             KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", p);
diff --git a/jdk/test/sun/security/pkcs11/ec/TestECDH.java b/jdk/test/sun/security/pkcs11/ec/TestECDH.java
index 8d950818560..421f46ccfc2 100644
--- a/jdk/test/sun/security/pkcs11/ec/TestECDH.java
+++ b/jdk/test/sun/security/pkcs11/ec/TestECDH.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2016, 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
@@ -28,16 +28,21 @@
  * @author Andreas Sterbenz
  * @library ..
  * @library ../../../../java/security/testlibrary
+ * @run main/othervm TestECDH
+ * @run main/othervm TestECDH sm policy
  */
 
-import java.io.*;
-import java.util.*;
-
-import java.security.*;
-import java.security.spec.*;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
 import java.security.interfaces.ECPublicKey;
-
-import javax.crypto.*;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Arrays;
+import javax.crypto.KeyAgreement;
 
 public class TestECDH extends PKCS11Test {
 
@@ -55,6 +60,7 @@ public class TestECDH extends PKCS11Test {
 
     private final static String secret163 = "04:ae:71:c1:c6:4d:f4:34:4d:72:70:a4:64:65:7f:2d:88:2d:3f:50:be";
 
+    @Override
     public void main(Provider p) throws Exception {
         if (p.getService("KeyAgreement", "ECDH") == null) {
             System.out.println("Provider does not support ECDH, skipping");
@@ -89,10 +95,12 @@ public class TestECDH extends PKCS11Test {
         System.out.println("OK");
     }
 
-    private final static void test(Provider p, String pub1s, String priv1s, String pub2s, String priv2s, String secrets) throws Exception {
+    private final static void test(Provider p, String pub1s, String priv1s,
+            String pub2s, String priv2s, String secrets) throws Exception {
         KeyFactory kf = KeyFactory.getInstance("EC", p);
         PublicKey pub1 = kf.generatePublic(new X509EncodedKeySpec(parse(pub1s)));
-        System.out.println("Testing using parameters " + ((ECPublicKey)pub1).getParams() + "...");
+        System.out.println("Testing using parameters "
+                + ((ECPublicKey)pub1).getParams() + "...");
 
         PrivateKey priv1 = kf.generatePrivate(new PKCS8EncodedKeySpec(parse(priv1s)));
         PublicKey pub2 = kf.generatePublic(new X509EncodedKeySpec(parse(pub2s)));
@@ -121,7 +129,7 @@ public class TestECDH extends PKCS11Test {
     }
 
     public static void main(String[] args) throws Exception {
-        main(new TestECDH());
+        main(new TestECDH(), args);
     }
 
 }
diff --git a/jdk/test/sun/security/pkcs11/ec/TestECDH2.java b/jdk/test/sun/security/pkcs11/ec/TestECDH2.java
index d5b03ae32aa..dee7a706ce7 100644
--- a/jdk/test/sun/security/pkcs11/ec/TestECDH2.java
+++ b/jdk/test/sun/security/pkcs11/ec/TestECDH2.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2016, 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
@@ -30,19 +30,25 @@
  * @library ../../../../java/security/testlibrary
  * @modules java.base/sun.security.util
  * @compile -XDignore.symbol.file TestECDH2.java
- * @run main TestECDH2
+ * @run main/othervm TestECDH2
+ * @run main/othervm TestECDH2 sm
  */
 
-import java.io.*;
-import java.util.*;
 import java.math.BigInteger;
-
-import java.security.*;
-import java.security.spec.*;
-import java.security.interfaces.*;
-import javax.crypto.*;
-
-import sun.security.util.ECUtil;
+import java.security.AlgorithmParameters;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.ECPublicKeySpec;
+import java.util.Arrays;
+import javax.crypto.KeyAgreement;
 
 public class TestECDH2 extends PKCS11Test {
 
@@ -81,7 +87,9 @@ public class TestECDH2 extends PKCS11Test {
 
     private KeyPair genECKeyPair(String curvName, String privD, String pubX,
                                  String pubY, Provider p) throws Exception {
-        ECParameterSpec ecParams = ECUtil.getECParameterSpec(p, curvName);
+        AlgorithmParameters params = AlgorithmParameters.getInstance("EC", p);
+        params.init(new ECGenParameterSpec(curvName));
+        ECParameterSpec ecParams = params.getParameterSpec(ECParameterSpec.class);
         ECPrivateKeySpec privKeySpec =
             new ECPrivateKeySpec(new BigInteger(privD, 16), ecParams);
         ECPublicKeySpec pubKeySpec =
@@ -98,19 +106,17 @@ public class TestECDH2 extends PKCS11Test {
         return kpg.generateKeyPair();
     }
     public static void main(String[] args) throws Exception {
-        main(new TestECDH2());
+        main(new TestECDH2(), args);
     }
 
+    @Override
     public void main(Provider provider) throws Exception {
         if (provider.getService("KeyAgreement", "ECDH") == null) {
             System.out.println("ECDH not supported, skipping");
             return;
         }
 
-        if (isNSS(provider) && getNSSVersion() >= 3.11 &&
-                getNSSVersion() < 3.12) {
-            System.out.println("NSS 3.11 has a DER issue that recent " +
-                    "version do not.");
+        if (isBadNSSVersion(provider)) {
             return;
         }
 
diff --git a/jdk/test/sun/security/pkcs11/ec/TestECDSA.java b/jdk/test/sun/security/pkcs11/ec/TestECDSA.java
index b1bd35f4783..9e0b8401aa9 100644
--- a/jdk/test/sun/security/pkcs11/ec/TestECDSA.java
+++ b/jdk/test/sun/security/pkcs11/ec/TestECDSA.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2016, 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
@@ -29,13 +29,22 @@
  * @library ..
  * @library ../../../../java/security/testlibrary
  * @key randomness
+ * @run main/othervm TestECDSA
+ * @run main/othervm TestECDSA sm policy
  */
 
-import java.util.*;
-
-import java.security.*;
-import java.security.spec.*;
-import java.security.interfaces.*;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.MessageDigest;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Random;
 
 public class TestECDSA extends PKCS11Test {
 
@@ -79,7 +88,8 @@ public class TestECDSA extends PKCS11Test {
     private final static byte[] data2Raw = {};
     private final static byte[] data2SHA = b("da:39:a3:ee:5e:6b:4b:0d:32:55:bf:ef:95:60:18:90:af:d8:07:09");
 
-    private static void verify(Provider provider, String alg, PublicKey key, byte[] data, byte[] sig, boolean result) throws Exception {
+    private static void verify(Provider provider, String alg, PublicKey key,
+            byte[] data, byte[] sig, boolean result) throws Exception {
         Signature s = Signature.getInstance(alg, provider);
         s.initVerify(key);
         boolean r;
@@ -105,9 +115,10 @@ public class TestECDSA extends PKCS11Test {
     }
 
     public static void main(String[] args) throws Exception {
-        main(new TestECDSA());
+        main(new TestECDSA(), args);
     }
 
+    @Override
     public void main(Provider provider) throws Exception {
         long start = System.currentTimeMillis();
 
@@ -116,25 +127,11 @@ public class TestECDSA extends PKCS11Test {
             return;
         }
 
-        if (isNSS(provider) && getNSSVersion() >= 3.11 &&
-                getNSSVersion() < 3.12) {
-            System.out.println("NSS 3.11 has a DER issue that recent " +
-                    "version do not.");
+        if (isBadNSSVersion(provider)) {
             return;
         }
 
-        /*
-         * Use Solaris SPARC 11.2 or later to avoid an intermittent failure
-         * when running SunPKCS11-Solaris (8044554)
-         */
-        if (provider.getName().equals("SunPKCS11-Solaris") &&
-            System.getProperty("os.name").equals("SunOS") &&
-            System.getProperty("os.arch").equals("sparcv9") &&
-            System.getProperty("os.version").compareTo("5.11") <= 0 &&
-            getDistro().compareTo("11.2") < 0) {
-
-            System.out.println("SunPKCS11-Solaris provider requires " +
-                "Solaris SPARC 11.2 or later, skipping");
+        if (isBadSolarisSparc(provider)) {
             return;
         }
 
diff --git a/jdk/test/sun/security/pkcs11/ec/TestECDSA2.java b/jdk/test/sun/security/pkcs11/ec/TestECDSA2.java
index b3f234a5b0f..c2cd7188a03 100644
--- a/jdk/test/sun/security/pkcs11/ec/TestECDSA2.java
+++ b/jdk/test/sun/security/pkcs11/ec/TestECDSA2.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2016, 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
@@ -30,18 +30,23 @@
  * @library ../../../../java/security/testlibrary
  * @modules java.base/sun.security.util
  * @compile -XDignore.symbol.file TestECDSA2.java
- * @run main TestECDSA2
+ * @run main/othervm TestECDSA2
+ * @run main/othervm TestECDSA2 sm
  */
 
-import java.io.*;
-import java.util.*;
 import java.math.BigInteger;
-
-import java.security.*;
-import java.security.spec.*;
-import java.security.interfaces.*;
-
-import sun.security.util.ECUtil;
+import java.security.AlgorithmParameters;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.ECPublicKeySpec;
 
 public class TestECDSA2 extends PKCS11Test {
 
@@ -78,7 +83,9 @@ public class TestECDSA2 extends PKCS11Test {
 
     private KeyPair genECKeyPair(String curvName, String privD, String pubX,
             String pubY, Provider p) throws Exception {
-        ECParameterSpec ecParams = ECUtil.getECParameterSpec(p, curvName);
+        AlgorithmParameters params = AlgorithmParameters.getInstance("EC", p);
+        params.init(new ECGenParameterSpec(curvName));
+        ECParameterSpec ecParams = params.getParameterSpec(ECParameterSpec.class);
         ECPrivateKeySpec privKeySpec =
             new ECPrivateKeySpec(new BigInteger(privD, 16), ecParams);
         ECPublicKeySpec pubKeySpec =
@@ -90,9 +97,10 @@ public class TestECDSA2 extends PKCS11Test {
     }
 
     public static void main(String[] args) throws Exception {
-        main(new TestECDSA2());
+        main(new TestECDSA2(), args);
     }
 
+    @Override
     public void main(Provider provider) throws Exception {
         boolean testP256 =
             (provider.getService("Signature", "SHA256withECDSA") != null);
@@ -105,10 +113,7 @@ public class TestECDSA2 extends PKCS11Test {
             return;
         }
 
-        if (isNSS(provider) && getNSSVersion() >= 3.11 &&
-                getNSSVersion() < 3.12) {
-            System.out.println("NSS 3.11 has a DER issue that recent " +
-                    "version do not.");
+        if (isBadNSSVersion(provider)) {
             return;
         }
 
diff --git a/jdk/test/sun/security/pkcs11/ec/TestECGenSpec.java b/jdk/test/sun/security/pkcs11/ec/TestECGenSpec.java
index 1dd2c326048..829b44ee7d9 100644
--- a/jdk/test/sun/security/pkcs11/ec/TestECGenSpec.java
+++ b/jdk/test/sun/security/pkcs11/ec/TestECGenSpec.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2016, 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,29 +27,32 @@
  * @summary Verify that we can use ECGenParameterSpec
  * @author Andreas Sterbenz
  * @library ..
+ * @run main/othervm TestECGenSpec
+ * @run main/othervm TestECGenSpec sm
  */
 
-import java.util.*;
-
-import java.security.*;
-import java.security.spec.*;
+import java.security.AlgorithmParameters;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.Provider;
 import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.ECParameterSpec;
 
 public class TestECGenSpec extends PKCS11Test {
 
     public static void main(String[] args) throws Exception {
-        main(new TestECGenSpec());
+        main(new TestECGenSpec(), args);
     }
 
+    @Override
     public void main(Provider p) throws Exception {
         if (p.getService("Signature", "SHA1withECDSA") == null) {
             System.out.println("Provider does not support ECDSA, skipping...");
             return;
         }
 
-        if (isNSS(p) && getNSSVersion() >= 3.11 && getNSSVersion() < 3.12) {
-            System.out.println("NSS 3.11 has a DER issue that recent " +
-                    "version do not.");
+        if (isBadNSSVersion(p)) {
             return;
         }
 
diff --git a/jdk/test/sun/security/pkcs11/ec/TestKeyFactory.java b/jdk/test/sun/security/pkcs11/ec/TestKeyFactory.java
index 6df2fbb1320..519c861ce3d 100644
--- a/jdk/test/sun/security/pkcs11/ec/TestKeyFactory.java
+++ b/jdk/test/sun/security/pkcs11/ec/TestKeyFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2016, 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,14 +27,23 @@
  * @summary Test the P11ECKeyFactory
  * @author Andreas Sterbenz
  * @library ..
+ * @run main/othervm TestKeyFactory
+ * @run main/othervm TestKeyFactory sm
  */
 
-import java.io.*;
-import java.util.*;
-
-import java.security.*;
-import java.security.interfaces.*;
-import java.security.spec.*;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.KeySpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Arrays;
 
 public class TestKeyFactory extends PKCS11Test {
 
@@ -111,9 +120,10 @@ public class TestKeyFactory extends PKCS11Test {
     }
 
     public static void main(String[] args) throws Exception {
-        main(new TestKeyFactory());
+        main(new TestKeyFactory(), args);
     }
 
+    @Override
     public void main(Provider p) throws Exception {
         if (p.getService("KeyFactory", "EC") == null) {
             System.out.println("Provider does not support EC, skipping");
diff --git a/jdk/test/sun/security/pkcs11/ec/policy b/jdk/test/sun/security/pkcs11/ec/policy
new file mode 100644
index 00000000000..c850c16bc58
--- /dev/null
+++ b/jdk/test/sun/security/pkcs11/ec/policy
@@ -0,0 +1,7 @@
+grant {
+    permission java.lang.RuntimePermission "setSecurityManager";
+    permission java.security.SecurityPermission "insertProvider.*";   
+    permission java.security.SecurityPermission "removeProvider.*";
+    permission java.io.FilePermission "${test.src}/-", "read";
+    permission java.io.FilePermission "${closed.base}/-", "read";
+};
\ No newline at end of file
diff --git a/jdk/test/sun/security/pkcs11/fips/TrustManagerTest.java b/jdk/test/sun/security/pkcs11/fips/TrustManagerTest.java
index 743b4562b87..619e8db530d 100644
--- a/jdk/test/sun/security/pkcs11/fips/TrustManagerTest.java
+++ b/jdk/test/sun/security/pkcs11/fips/TrustManagerTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2016, 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
@@ -29,15 +29,21 @@
  * @library ..
  * @modules java.base/com.sun.net.ssl.internal.ssl
  * @run main/othervm TrustManagerTest
+ * @run main/othervm TrustManagerTest sm TrustManagerTest.policy
  */
 
-import java.io.*;
-import java.util.*;
-
-import java.security.*;
-import java.security.cert.*;
-
-import javax.net.ssl.*;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.security.KeyStore;
+import java.security.Policy;
+import java.security.Provider;
+import java.security.Security;
+import java.security.URIParameter;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
 
 // This test belongs more in JSSE than here, but the JSSE workspace does not
 // have the NSS test infrastructure. It will live here for the time being.
@@ -73,6 +79,12 @@ public class TrustManagerTest extends SecmodTest {
         X509Certificate ca = loadCertificate("certs/ca.cer");
         X509Certificate anchor = loadCertificate("certs/anchor.cer");
 
+        if (args.length > 1 && "sm".equals(args[0])) {
+            Policy.setPolicy(Policy.getInstance("JavaPolicy",
+                    new URIParameter(new File(BASE, args[1]).toURI())));
+            System.setSecurityManager(new SecurityManager());
+        }
+
         KeyStore trustStore = KeyStore.getInstance("JKS");
         trustStore.load(null, null);
         trustStore.setCertificateEntry("anchor", anchor);
@@ -90,11 +102,10 @@ public class TrustManagerTest extends SecmodTest {
     }
 
     private static X509Certificate loadCertificate(String name) throws Exception {
-        CertificateFactory cf = CertificateFactory.getInstance("X.509");
-        InputStream in = new FileInputStream(BASE + SEP + name);
-        X509Certificate cert = (X509Certificate)cf.generateCertificate(in);
-        in.close();
-        return cert;
+        try (InputStream in = new FileInputStream(BASE + SEP + name)) {
+            return (X509Certificate) CertificateFactory.getInstance("X.509")
+                    .generateCertificate(in);
+        }
     }
 
 }
diff --git a/jdk/test/sun/security/pkcs11/fips/TrustManagerTest.policy b/jdk/test/sun/security/pkcs11/fips/TrustManagerTest.policy
new file mode 100644
index 00000000000..16bb57d4e1b
--- /dev/null
+++ b/jdk/test/sun/security/pkcs11/fips/TrustManagerTest.policy
@@ -0,0 +1,3 @@
+grant {
+    
+};
\ No newline at end of file
diff --git a/jdk/test/sun/security/pkcs11/policy b/jdk/test/sun/security/pkcs11/policy
new file mode 100644
index 00000000000..54281a78179
--- /dev/null
+++ b/jdk/test/sun/security/pkcs11/policy
@@ -0,0 +1,3 @@
+grant {
+    permission java.lang.RuntimePermission "setSecurityManager";
+};
\ No newline at end of file
diff --git a/jdk/test/sun/security/pkcs11/rsa/KeyWrap.java b/jdk/test/sun/security/pkcs11/rsa/KeyWrap.java
index 7f08481d6f7..e0bad7cd2d4 100644
--- a/jdk/test/sun/security/pkcs11/rsa/KeyWrap.java
+++ b/jdk/test/sun/security/pkcs11/rsa/KeyWrap.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2007, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2016, 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
@@ -28,18 +28,28 @@
  * @author Andreas Sterbenz
  * @library ..
  * @key randomness
+ * @run main/othervm KeyWrap
+ * @run main/othervm KeyWrap sm
  */
 
-import java.io.*;
-import java.util.*;
-
-import java.security.*;
-
-import javax.crypto.*;
-import javax.crypto.spec.*;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.util.Random;
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
 
 public class KeyWrap extends PKCS11Test {
 
+    @Override
     public void main(Provider p) throws Exception {
         try {
             Cipher.getInstance("RSA/ECB/PKCS1Padding", p);
@@ -62,7 +72,7 @@ public class KeyWrap extends PKCS11Test {
                 PublicKey pub = (PublicKey)kf.translateKey(kp.getPublic());
                 PrivateKey priv = (PrivateKey)kf.translateKey(kp.getPrivate());
                 kp = new KeyPair(pub, priv);
-            } catch (Exception ee) {
+            } catch (NoSuchAlgorithmException | InvalidKeyException ee) {
                 ee.printStackTrace();
                 System.out.println("Provider does not support RSA, skipping");
                 return;
@@ -93,7 +103,7 @@ public class KeyWrap extends PKCS11Test {
     }
 
     public static void main(String[] args) throws Exception {
-        main(new KeyWrap());
+        main(new KeyWrap(), args);
     }
 
 }
diff --git a/jdk/test/sun/security/pkcs11/rsa/TestCACerts.java b/jdk/test/sun/security/pkcs11/rsa/TestCACerts.java
index cd845c68201..24c16243585 100644
--- a/jdk/test/sun/security/pkcs11/rsa/TestCACerts.java
+++ b/jdk/test/sun/security/pkcs11/rsa/TestCACerts.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2016, 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
@@ -28,24 +28,28 @@
  * @author Andreas Sterbenz
  * @library ..
  * @library ../../../../java/security/testlibrary
+ * @run main/othervm TestCACerts
+ * @run main/othervm TestCACerts sm TestCACerts.policy
  */
 
 // this test serves as our known answer test
 
-import java.io.*;
-import java.util.*;
-
-import java.security.*;
-import java.security.cert.*;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.security.KeyStore;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.Security;
+import java.security.cert.X509Certificate;
+import java.util.Enumeration;
 
 public class TestCACerts extends PKCS11Test {
 
-    private final static char SEP = File.separatorChar;
-
     public static void main(String[] args) throws Exception {
-        main(new TestCACerts());
+        main(new TestCACerts(), args);
     }
 
+    @Override
     public void main(Provider p) throws Exception {
 
         /*
@@ -53,9 +57,9 @@ public class TestCACerts extends PKCS11Test {
          * when running SunPKCS11-Solaris (8044554)
          */
         if (p.getName().equals("SunPKCS11-Solaris") &&
-            System.getProperty("os.name").equals("SunOS") &&
-            System.getProperty("os.arch").equals("sparcv9") &&
-            System.getProperty("os.version").compareTo("5.11") <= 0 &&
+            props.getProperty("os.name").equals("SunOS") &&
+            props.getProperty("os.arch").equals("sparcv9") &&
+            props.getProperty("os.version").compareTo("5.11") <= 0 &&
             getDistro().compareTo("11.2") < 0) {
 
             System.out.println("SunPKCS11-Solaris provider requires " +
@@ -67,12 +71,13 @@ public class TestCACerts extends PKCS11Test {
         Providers.setAt(p, 1);
         try {
             String PROVIDER = p.getName();
-            String javaHome = System.getProperty("java.home");
+            String javaHome = props.getProperty("java.home");
             String caCerts = javaHome + SEP + "lib" + SEP + "security" + SEP + "cacerts";
-            InputStream in = new FileInputStream(caCerts);
-            KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
-            ks.load(in, null);
-            in.close();
+            KeyStore ks;
+            try (InputStream in = new FileInputStream(caCerts)) {
+                ks = KeyStore.getInstance(KeyStore.getDefaultType());
+                ks.load(in, null);
+            }
             for (Enumeration e = ks.aliases(); e.hasMoreElements(); ) {
                 String alias = (String)e.nextElement();
                 if (ks.isCertificateEntry(alias)) {
diff --git a/jdk/test/sun/security/pkcs11/rsa/TestCACerts.policy b/jdk/test/sun/security/pkcs11/rsa/TestCACerts.policy
new file mode 100644
index 00000000000..37f028361bc
--- /dev/null
+++ b/jdk/test/sun/security/pkcs11/rsa/TestCACerts.policy
@@ -0,0 +1,7 @@
+grant {
+    permission java.lang.RuntimePermission "setSecurityManager";
+    permission java.security.SecurityPermission "insertProvider.*";
+    permission java.security.SecurityPermission "removeProvider.*";
+    permission java.util.PropertyPermission "java.home", "read";
+    permission java.io.FilePermission "${java.home}/lib/security/cacerts", "read";
+};
\ No newline at end of file
diff --git a/jdk/test/sun/security/pkcs11/rsa/TestKeyFactory.java b/jdk/test/sun/security/pkcs11/rsa/TestKeyFactory.java
index 049be1046f9..802774285c3 100644
--- a/jdk/test/sun/security/pkcs11/rsa/TestKeyFactory.java
+++ b/jdk/test/sun/security/pkcs11/rsa/TestKeyFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2016, 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,26 +27,26 @@
  * @summary Test KeyFactory of the new RSA provider
  * @author Andreas Sterbenz
  * @library ..
+ * @run main/othervm TestKeyFactory
+ * @run main/othervm TestKeyFactory sm rsakeys.ks.policy
  */
 
 import java.io.*;
 import java.util.*;
 
 import java.security.*;
-import java.security.interfaces.*;
 import java.security.spec.*;
 
 public class TestKeyFactory extends PKCS11Test {
 
-    private final static String BASE = System.getProperty("test.src", ".");
-
     private static final char[] password = "test12".toCharArray();
 
     static KeyStore getKeyStore() throws Exception {
-        InputStream in = new FileInputStream(new File(BASE, "rsakeys.ks"));
-        KeyStore ks = KeyStore.getInstance("JKS");
-        ks.load(in, password);
-        in.close();
+        KeyStore ks;
+        try (InputStream in = new FileInputStream(new File(BASE, "rsakeys.ks"))) {
+            ks = KeyStore.getInstance("JKS");
+            ks.load(in, password);
+        }
         return ks;
     }
 
@@ -128,9 +128,10 @@ public class TestKeyFactory extends PKCS11Test {
     }
 
     public static void main(String[] args) throws Exception {
-        main(new TestKeyFactory());
+        main(new TestKeyFactory(), args);
     }
 
+    @Override
     public void main(Provider p) throws Exception {
         long start = System.currentTimeMillis();
         KeyStore ks = getKeyStore();
diff --git a/jdk/test/sun/security/pkcs11/rsa/TestKeyPairGenerator.java b/jdk/test/sun/security/pkcs11/rsa/TestKeyPairGenerator.java
index 655edd3b5a2..687a7a87bc1 100644
--- a/jdk/test/sun/security/pkcs11/rsa/TestKeyPairGenerator.java
+++ b/jdk/test/sun/security/pkcs11/rsa/TestKeyPairGenerator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2016, 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
@@ -30,16 +30,20 @@
  * @library /lib/testlibrary
  * @build jdk.testlibrary.*
  * @run main/othervm TestKeyPairGenerator
+ * @run main/othervm TestKeyPairGenerator sm TestKeyPairGenerator.policy
  * @key intermittent randomness
  */
 
-import java.io.*;
-import java.util.*;
 import java.math.BigInteger;
-
-import java.security.*;
-import java.security.interfaces.*;
-import java.security.spec.*;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.RSAKeyGenParameterSpec;
 import jdk.testlibrary.RandomFactory;
 
 public class TestKeyPairGenerator extends PKCS11Test {
@@ -48,7 +52,8 @@ public class TestKeyPairGenerator extends PKCS11Test {
 
     private static byte[] data;
 
-    private static void testSignature(String algorithm, PrivateKey privateKey, PublicKey publicKey) throws Exception {
+    private static void testSignature(String algorithm, PrivateKey privateKey,
+            PublicKey publicKey) throws Exception {
         System.out.println("Testing " + algorithm + "...");
         Signature s = Signature.getInstance(algorithm, provider);
         s.initSign(privateKey);
@@ -98,9 +103,10 @@ public class TestKeyPairGenerator extends PKCS11Test {
     }
 
     public static void main(String[] args) throws Exception {
-        main(new TestKeyPairGenerator());
+        main(new TestKeyPairGenerator(), args);
     }
 
+    @Override
     public void main(Provider p) throws Exception {
         long start = System.currentTimeMillis();
         provider = p;
diff --git a/jdk/test/sun/security/pkcs11/rsa/TestKeyPairGenerator.policy b/jdk/test/sun/security/pkcs11/rsa/TestKeyPairGenerator.policy
new file mode 100644
index 00000000000..3f076e14679
--- /dev/null
+++ b/jdk/test/sun/security/pkcs11/rsa/TestKeyPairGenerator.policy
@@ -0,0 +1,4 @@
+grant {
+    permission java.lang.RuntimePermission "setSecurityManager";
+    permission java.util.PropertyPermission "seed", "read";
+};
\ No newline at end of file
diff --git a/jdk/test/sun/security/pkcs11/rsa/TestSignatures.java b/jdk/test/sun/security/pkcs11/rsa/TestSignatures.java
index a6070ea675d..ae4718f4f56 100644
--- a/jdk/test/sun/security/pkcs11/rsa/TestSignatures.java
+++ b/jdk/test/sun/security/pkcs11/rsa/TestSignatures.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2016, 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
@@ -28,18 +28,25 @@
  * @author Andreas Sterbenz
  * @library ..
  * @key randomness
+ * @run main/othervm TestSignatures
+ * @run main/othervm TestSignatures sm rsakeys.ks.policy
  */
 
-import java.io.*;
-import java.util.*;
-
-import java.security.*;
-import java.security.interfaces.*;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.security.KeyFactory;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.interfaces.RSAPublicKey;
+import java.util.Enumeration;
+import java.util.Random;
 
 public class TestSignatures extends PKCS11Test {
 
-    private final static String BASE = System.getProperty("test.src", ".");
-
     private static final char[] password = "test12".toCharArray();
 
     private static Provider provider;
@@ -47,14 +54,16 @@ public class TestSignatures extends PKCS11Test {
     private static byte[] data;
 
     static KeyStore getKeyStore() throws Exception {
-        InputStream in = new FileInputStream(new File(BASE, "rsakeys.ks"));
-        KeyStore ks = KeyStore.getInstance("JKS");
-        ks.load(in, password);
-        in.close();
+        KeyStore ks;
+        try (InputStream in = new FileInputStream(new File(BASE, "rsakeys.ks"))) {
+            ks = KeyStore.getInstance("JKS");
+            ks.load(in, password);
+        }
         return ks;
     }
 
-    private static void testSignature(String algorithm, PrivateKey privateKey, PublicKey publicKey) throws Exception {
+    private static void testSignature(String algorithm, PrivateKey privateKey,
+            PublicKey publicKey) throws Exception {
         System.out.println("Testing " + algorithm + "...");
         Signature s = Signature.getInstance(algorithm, provider);
         s.initSign(privateKey);
@@ -78,7 +87,8 @@ public class TestSignatures extends PKCS11Test {
         }
     }
 
-    private static void test(PrivateKey privateKey, PublicKey publicKey) throws Exception {
+    private static void test(PrivateKey privateKey, PublicKey publicKey)
+            throws Exception {
         testSignature("MD2withRSA", privateKey, publicKey);
         testSignature("MD5withRSA", privateKey, publicKey);
         testSignature("SHA1withRSA", privateKey, publicKey);
@@ -93,9 +103,10 @@ public class TestSignatures extends PKCS11Test {
     }
 
     public static void main(String[] args) throws Exception {
-        main(new TestSignatures());
+        main(new TestSignatures(), args);
     }
 
+    @Override
     public void main(Provider p) throws Exception {
 
         /*
@@ -103,9 +114,9 @@ public class TestSignatures extends PKCS11Test {
          * when running SunPKCS11-Solaris (8044554)
          */
         if (p.getName().equals("SunPKCS11-Solaris") &&
-            System.getProperty("os.name").equals("SunOS") &&
-            System.getProperty("os.arch").equals("sparcv9") &&
-            System.getProperty("os.version").compareTo("5.11") <= 0 &&
+            props.getProperty("os.name").equals("SunOS") &&
+            props.getProperty("os.arch").equals("sparcv9") &&
+            props.getProperty("os.version").compareTo("5.11") <= 0 &&
             getDistro().compareTo("11.2") < 0) {
 
             System.out.println("SunPKCS11-Solaris provider requires " +
diff --git a/jdk/test/sun/security/pkcs11/rsa/rsakeys.ks.policy b/jdk/test/sun/security/pkcs11/rsa/rsakeys.ks.policy
new file mode 100644
index 00000000000..4a0b0d2c46d
--- /dev/null
+++ b/jdk/test/sun/security/pkcs11/rsa/rsakeys.ks.policy
@@ -0,0 +1,4 @@
+grant {
+    permission java.lang.RuntimePermission "setSecurityManager";
+    permission java.io.FilePermission "${test.src}/rsakeys.ks", "read";
+};
\ No newline at end of file
diff --git a/jdk/test/sun/security/pkcs11/sslecc/CipherTest.java b/jdk/test/sun/security/pkcs11/sslecc/CipherTest.java
index 4ec23743b2a..f118d76d5c5 100644
--- a/jdk/test/sun/security/pkcs11/sslecc/CipherTest.java
+++ b/jdk/test/sun/security/pkcs11/sslecc/CipherTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2016, 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
@@ -47,7 +47,8 @@ public class CipherTest {
     // use any available port for the server socket
     static volatile int serverPort = 0;
 
-    final int THREADS;
+    static final int THREADS = Integer.getInteger("numThreads", 4);
+    static final String TEST_SRC = System.getProperty("test.src", ".");
 
     // assume that if we do not read anything for 20 seconds, something
     // has gone wrong
@@ -68,6 +69,7 @@ public class CipherTest {
             this.cipherTest = cipherTest;
         }
 
+        @Override
         public abstract void run();
 
         void handleRequest(InputStream in, OutputStream out) throws IOException {
@@ -117,6 +119,7 @@ public class CipherTest {
             return TLSCipherStatus.isEnabled(cipherSuite, protocol);
         }
 
+        @Override
         public String toString() {
             String s = cipherSuite + " in " + protocol + " mode";
             if (clientAuth != null) {
@@ -260,7 +263,6 @@ public class CipherTest {
     private boolean failed;
 
     private CipherTest(PeerFactory peerFactory) throws IOException {
-        THREADS = Integer.parseInt(System.getProperty("numThreads", "4"));
         factory = (SSLSocketFactory)SSLSocketFactory.getDefault();
         SSLSocket socket = (SSLSocket)factory.createSocket();
         String[] cipherSuites = socket.getSupportedCipherSuites();
@@ -350,6 +352,7 @@ public class CipherTest {
             this.cipherTest = cipherTest;
         }
 
+        @Override
         public final void run() {
             while (true) {
                 TestParameters params = cipherTest.getTest();
@@ -405,10 +408,11 @@ public class CipherTest {
 
     private static KeyStore readKeyStore(String name) throws Exception {
         File file = new File(PATH, name);
-        InputStream in = new FileInputStream(file);
-        KeyStore ks = KeyStore.getInstance("JKS");
-        ks.load(in, passwd);
-        in.close();
+        KeyStore ks;
+        try (InputStream in = new FileInputStream(file)) {
+            ks = KeyStore.getInstance("JKS");
+            ks.load(in, passwd);
+        }
         return ks;
     }
 
@@ -421,7 +425,7 @@ public class CipherTest {
         } else {
             relPath = pathToStores;
         }
-        PATH = new File(System.getProperty("test.src", "."), relPath);
+        PATH = new File(TEST_SRC, relPath);
         CipherTest.peerFactory = peerFactory;
         System.out.print(
             "Initializing test '" + peerFactory.getName() + "'...");
@@ -494,16 +498,19 @@ class AlwaysTrustManager implements X509TrustManager {
 
     }
 
+    @Override
     public void checkClientTrusted(X509Certificate[] chain, String authType)
             throws CertificateException {
         // empty
     }
 
+    @Override
     public void checkServerTrusted(X509Certificate[] chain, String authType)
             throws CertificateException {
         // empty
     }
 
+    @Override
     public X509Certificate[] getAcceptedIssuers() {
         return new X509Certificate[0];
     }
@@ -522,6 +529,7 @@ class MyX509KeyManager extends X509ExtendedKeyManager {
         this.authType = "ECDSA".equals(authType) ? "EC" : authType;
     }
 
+    @Override
     public String[] getClientAliases(String keyType, Principal[] issuers) {
         if (authType == null) {
             return null;
@@ -529,6 +537,7 @@ class MyX509KeyManager extends X509ExtendedKeyManager {
         return keyManager.getClientAliases(authType, issuers);
     }
 
+    @Override
     public String chooseClientAlias(String[] keyType, Principal[] issuers,
             Socket socket) {
         if (authType == null) {
@@ -538,6 +547,7 @@ class MyX509KeyManager extends X509ExtendedKeyManager {
             issuers, socket);
     }
 
+    @Override
     public String chooseEngineClientAlias(String[] keyType,
             Principal[] issuers, SSLEngine engine) {
         if (authType == null) {
@@ -547,24 +557,29 @@ class MyX509KeyManager extends X509ExtendedKeyManager {
             issuers, engine);
     }
 
+    @Override
     public String[] getServerAliases(String keyType, Principal[] issuers) {
         throw new UnsupportedOperationException("Servers not supported");
     }
 
+    @Override
     public String chooseServerAlias(String keyType, Principal[] issuers,
             Socket socket) {
         throw new UnsupportedOperationException("Servers not supported");
     }
 
+    @Override
     public String chooseEngineServerAlias(String keyType, Principal[] issuers,
             SSLEngine engine) {
         throw new UnsupportedOperationException("Servers not supported");
     }
 
+    @Override
     public X509Certificate[] getCertificateChain(String alias) {
         return keyManager.getCertificateChain(alias);
     }
 
+    @Override
     public PrivateKey getPrivateKey(String alias) {
         return keyManager.getPrivateKey(alias);
     }
@@ -577,6 +592,7 @@ class DaemonThreadFactory implements ThreadFactory {
 
     private final static ThreadFactory DEFAULT = Executors.defaultThreadFactory();
 
+    @Override
     public Thread newThread(Runnable r) {
         Thread t = DEFAULT.newThread(r);
         t.setDaemon(true);
diff --git a/jdk/test/sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java b/jdk/test/sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java
index dea0495cd13..4a31b67c296 100644
--- a/jdk/test/sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java
+++ b/jdk/test/sun/security/pkcs11/sslecc/ClientJSSEServerJSSE.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -34,25 +34,28 @@
  * @library ..
  * @library ../../../../java/security/testlibrary
  * @run main/othervm ClientJSSEServerJSSE
+ * @run main/othervm ClientJSSEServerJSSE sm policy
  */
 
-import java.security.*;
+import java.security.Provider;
+import java.security.Security;
 
 public class ClientJSSEServerJSSE extends PKCS11Test {
 
     private static String[] cmdArgs;
 
     public static void main(String[] args) throws Exception {
-        cmdArgs = args;
-        main(new ClientJSSEServerJSSE());
-    }
-
-    public void main(Provider p) throws Exception {
         // reset security properties to make sure that the algorithms
         // and keys used in this test are not disabled.
         Security.setProperty("jdk.tls.disabledAlgorithms", "");
         Security.setProperty("jdk.certpath.disabledAlgorithms", "");
 
+        cmdArgs = args;
+        main(new ClientJSSEServerJSSE(), args);
+    }
+
+    @Override
+    public void main(Provider p) throws Exception {
         if (p.getService("KeyFactory", "EC") == null) {
             System.out.println("Provider does not support EC, skipping");
             return;
@@ -64,14 +67,17 @@ public class ClientJSSEServerJSSE extends PKCS11Test {
 
     private static class JSSEFactory extends CipherTest.PeerFactory {
 
+        @Override
         String getName() {
             return "Client JSSE - Server JSSE";
         }
 
+        @Override
         CipherTest.Client newClient(CipherTest cipherTest) throws Exception {
             return new JSSEClient(cipherTest);
         }
 
+        @Override
         CipherTest.Server newServer(CipherTest cipherTest) throws Exception {
             return new JSSEServer(cipherTest);
         }
diff --git a/jdk/test/sun/security/pkcs11/sslecc/JSSEServer.java b/jdk/test/sun/security/pkcs11/sslecc/JSSEServer.java
index af8d4b5a088..90c55e68e4a 100644
--- a/jdk/test/sun/security/pkcs11/sslecc/JSSEServer.java
+++ b/jdk/test/sun/security/pkcs11/sslecc/JSSEServer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2016, 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
@@ -21,16 +21,17 @@
  * questions.
  */
 
-import java.io.*;
-import java.net.*;
-import java.util.*;
-import java.util.concurrent.*;
-
-import java.security.*;
-import java.security.cert.*;
-import java.security.cert.Certificate;
-
-import javax.net.ssl.*;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.TrustManager;
 
 class JSSEServer extends CipherTest.Server {
 
@@ -48,15 +49,17 @@ class JSSEServer extends CipherTest.Server {
         serverSocket.setWantClientAuth(true);
     }
 
+    @Override
     public void run() {
         System.out.println("JSSE Server listening on port " + cipherTest.serverPort);
         Executor exec = Executors.newFixedThreadPool
-                            (cipherTest.THREADS, DaemonThreadFactory.INSTANCE);
+                            (CipherTest.THREADS, DaemonThreadFactory.INSTANCE);
         try {
             while (true) {
                 final SSLSocket socket = (SSLSocket)serverSocket.accept();
                 socket.setSoTimeout(cipherTest.TIMEOUT);
                 Runnable r = new Runnable() {
+                    @Override
                     public void run() {
                         try {
                             InputStream in = socket.getInputStream();
diff --git a/jdk/test/sun/security/pkcs11/sslecc/policy b/jdk/test/sun/security/pkcs11/sslecc/policy
new file mode 100644
index 00000000000..f95da0407a7
--- /dev/null
+++ b/jdk/test/sun/security/pkcs11/sslecc/policy
@@ -0,0 +1,9 @@
+grant {
+    permission java.lang.RuntimePermission "setSecurityManager";
+    permission java.security.SecurityPermission "insertProvider.*";
+    permission java.security.SecurityPermission "removeProvider.*";
+    permission java.util.PropertyPermission "test.src", "read";
+    permission java.util.PropertyPermission "numThreads", "read";
+    permission java.io.FilePermission "${test.src}/*", "read";
+    permission java.net.SocketPermission "127.0.0.1:*", "listen,resolve,accept,connect";
+};
\ No newline at end of file
diff --git a/jdk/test/sun/security/pkcs11/tls/TestKeyMaterial.java b/jdk/test/sun/security/pkcs11/tls/TestKeyMaterial.java
index 99636d0e2aa..1511dff4d4b 100644
--- a/jdk/test/sun/security/pkcs11/tls/TestKeyMaterial.java
+++ b/jdk/test/sun/security/pkcs11/tls/TestKeyMaterial.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2016, 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
@@ -28,136 +28,138 @@
  * @author Andreas Sterbenz
  * @library ..
  * @modules java.base/sun.security.internal.spec
+ * @run main/othervm TestKeyMaterial
+ * @run main/othervm TestKeyMaterial sm policy
  */
 
-import java.io.*;
-import java.util.*;
-
-import java.security.Security;
+import java.io.BufferedReader;
+import java.nio.file.Files;
+import java.nio.file.Paths;
 import java.security.Provider;
-
+import java.util.Arrays;
 import javax.crypto.KeyGenerator;
 import javax.crypto.SecretKey;
-
-import javax.crypto.spec.*;
-
-import sun.security.internal.spec.*;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import sun.security.internal.spec.TlsKeyMaterialParameterSpec;
+import sun.security.internal.spec.TlsKeyMaterialSpec;
 
 public class TestKeyMaterial extends PKCS11Test {
 
-    private static int PREFIX_LENGTH = "km-master:  ".length();
+    private static final int PREFIX_LENGTH = "km-master:  ".length();
 
     public static void main(String[] args) throws Exception {
-        main(new TestKeyMaterial());
+        main(new TestKeyMaterial(), args);
     }
 
+    @Override
     public void main(Provider provider) throws Exception {
         if (provider.getService("KeyGenerator", "SunTlsKeyMaterial") == null) {
             System.out.println("Provider does not support algorithm, skipping");
             return;
         }
 
-        InputStream in = new FileInputStream(new File(BASE, "keymatdata.txt"));
-        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+        try (BufferedReader reader = Files.newBufferedReader(
+                Paths.get(BASE, "keymatdata.txt"))) {
 
-        int n = 0;
-        int lineNumber = 0;
+            int n = 0;
+            int lineNumber = 0;
 
-        byte[] master = null;
-        int major = 0;
-        int minor = 0;
-        byte[] clientRandom = null;
-        byte[] serverRandom = null;
-        String cipherAlgorithm = null;
-        int keyLength = 0;
-        int expandedKeyLength = 0;
-        int ivLength = 0;
-        int macLength = 0;
-        byte[] clientCipherBytes = null;
-        byte[] serverCipherBytes = null;
-        byte[] clientIv = null;
-        byte[] serverIv = null;
-        byte[] clientMacBytes = null;
-        byte[] serverMacBytes = null;
+            byte[] master = null;
+            int major = 0;
+            int minor = 0;
+            byte[] clientRandom = null;
+            byte[] serverRandom = null;
+            String cipherAlgorithm = null;
+            int keyLength = 0;
+            int expandedKeyLength = 0;
+            int ivLength = 0;
+            int macLength = 0;
+            byte[] clientCipherBytes = null;
+            byte[] serverCipherBytes = null;
+            byte[] clientIv = null;
+            byte[] serverIv = null;
+            byte[] clientMacBytes = null;
+            byte[] serverMacBytes = null;
 
-        while (true) {
-            String line = reader.readLine();
-            lineNumber++;
-            if (line == null) {
-                break;
+            while (true) {
+                String line = reader.readLine();
+                lineNumber++;
+                if (line == null) {
+                    break;
+                }
+                if (line.startsWith("km-") == false) {
+                    continue;
+                }
+                String data = line.substring(PREFIX_LENGTH);
+                if (line.startsWith("km-master:")) {
+                    master = parse(data);
+                } else if (line.startsWith("km-major:")) {
+                    major = Integer.parseInt(data);
+                } else if (line.startsWith("km-minor:")) {
+                    minor = Integer.parseInt(data);
+                } else if (line.startsWith("km-crandom:")) {
+                    clientRandom = parse(data);
+                } else if (line.startsWith("km-srandom:")) {
+                    serverRandom = parse(data);
+                } else if (line.startsWith("km-cipalg:")) {
+                    cipherAlgorithm = data;
+                } else if (line.startsWith("km-keylen:")) {
+                    keyLength = Integer.parseInt(data);
+                } else if (line.startsWith("km-explen:")) {
+                    expandedKeyLength = Integer.parseInt(data);
+                } else if (line.startsWith("km-ivlen:")) {
+                    ivLength = Integer.parseInt(data);
+                } else if (line.startsWith("km-maclen:")) {
+                    macLength = Integer.parseInt(data);
+                } else if (line.startsWith("km-ccipkey:")) {
+                    clientCipherBytes = parse(data);
+                } else if (line.startsWith("km-scipkey:")) {
+                    serverCipherBytes = parse(data);
+                } else if (line.startsWith("km-civ:")) {
+                    clientIv = parse(data);
+                } else if (line.startsWith("km-siv:")) {
+                    serverIv = parse(data);
+                } else if (line.startsWith("km-cmackey:")) {
+                    clientMacBytes = parse(data);
+                } else if (line.startsWith("km-smackey:")) {
+                    serverMacBytes = parse(data);
+
+                    System.out.print(".");
+                    n++;
+
+                    KeyGenerator kg =
+                        KeyGenerator.getInstance("SunTlsKeyMaterial", provider);
+                    SecretKey masterKey =
+                        new SecretKeySpec(master, "TlsMasterSecret");
+                    TlsKeyMaterialParameterSpec spec =
+                        new TlsKeyMaterialParameterSpec(masterKey, major, minor,
+                        clientRandom, serverRandom, cipherAlgorithm,
+                        keyLength, expandedKeyLength, ivLength, macLength,
+                        null, -1, -1);
+
+                    kg.init(spec);
+                    TlsKeyMaterialSpec result =
+                        (TlsKeyMaterialSpec)kg.generateKey();
+                    match(lineNumber, clientCipherBytes,
+                        result.getClientCipherKey(), cipherAlgorithm);
+                    match(lineNumber, serverCipherBytes,
+                        result.getServerCipherKey(), cipherAlgorithm);
+                    match(lineNumber, clientIv, result.getClientIv(), "");
+                    match(lineNumber, serverIv, result.getServerIv(), "");
+                    match(lineNumber, clientMacBytes, result.getClientMacKey(), "");
+                    match(lineNumber, serverMacBytes, result.getServerMacKey(), "");
+
+                } else {
+                    throw new Exception("Unknown line: " + line);
+                }
             }
-            if (line.startsWith("km-") == false) {
-                continue;
-            }
-            String data = line.substring(PREFIX_LENGTH);
-            if (line.startsWith("km-master:")) {
-                master = parse(data);
-            } else if (line.startsWith("km-major:")) {
-                major = Integer.parseInt(data);
-            } else if (line.startsWith("km-minor:")) {
-                minor = Integer.parseInt(data);
-            } else if (line.startsWith("km-crandom:")) {
-                clientRandom = parse(data);
-            } else if (line.startsWith("km-srandom:")) {
-                serverRandom = parse(data);
-            } else if (line.startsWith("km-cipalg:")) {
-                cipherAlgorithm = data;
-            } else if (line.startsWith("km-keylen:")) {
-                keyLength = Integer.parseInt(data);
-            } else if (line.startsWith("km-explen:")) {
-                expandedKeyLength = Integer.parseInt(data);
-            } else if (line.startsWith("km-ivlen:")) {
-                ivLength = Integer.parseInt(data);
-            } else if (line.startsWith("km-maclen:")) {
-                macLength = Integer.parseInt(data);
-            } else if (line.startsWith("km-ccipkey:")) {
-                clientCipherBytes = parse(data);
-            } else if (line.startsWith("km-scipkey:")) {
-                serverCipherBytes = parse(data);
-            } else if (line.startsWith("km-civ:")) {
-                clientIv = parse(data);
-            } else if (line.startsWith("km-siv:")) {
-                serverIv = parse(data);
-            } else if (line.startsWith("km-cmackey:")) {
-                clientMacBytes = parse(data);
-            } else if (line.startsWith("km-smackey:")) {
-                serverMacBytes = parse(data);
-
-                System.out.print(".");
-                n++;
-
-                KeyGenerator kg =
-                    KeyGenerator.getInstance("SunTlsKeyMaterial", provider);
-                SecretKey masterKey =
-                    new SecretKeySpec(master, "TlsMasterSecret");
-                TlsKeyMaterialParameterSpec spec =
-                    new TlsKeyMaterialParameterSpec(masterKey, major, minor,
-                    clientRandom, serverRandom, cipherAlgorithm,
-                    keyLength, expandedKeyLength, ivLength, macLength,
-                    null, -1, -1);
-
-                kg.init(spec);
-                TlsKeyMaterialSpec result =
-                    (TlsKeyMaterialSpec)kg.generateKey();
-                match(lineNumber, clientCipherBytes,
-                    result.getClientCipherKey(), cipherAlgorithm);
-                match(lineNumber, serverCipherBytes,
-                    result.getServerCipherKey(), cipherAlgorithm);
-                match(lineNumber, clientIv, result.getClientIv(), "");
-                match(lineNumber, serverIv, result.getServerIv(), "");
-                match(lineNumber, clientMacBytes, result.getClientMacKey(), "");
-                match(lineNumber, serverMacBytes, result.getServerMacKey(), "");
-
-            } else {
-                throw new Exception("Unknown line: " + line);
+            if (n == 0) {
+                throw new Exception("no tests");
             }
+            System.out.println();
+            System.out.println("OK: " + n + " tests");
         }
-        if (n == 0) {
-            throw new Exception("no tests");
-        }
-        in.close();
-        System.out.println();
-        System.out.println("OK: " + n + " tests");
     }
 
     private static void stripParity(byte[] b) {
diff --git a/jdk/test/sun/security/pkcs11/tls/TestLeadingZeroesP11.java b/jdk/test/sun/security/pkcs11/tls/TestLeadingZeroesP11.java
index ffaac041d33..a8d8f72a299 100644
--- a/jdk/test/sun/security/pkcs11/tls/TestLeadingZeroesP11.java
+++ b/jdk/test/sun/security/pkcs11/tls/TestLeadingZeroesP11.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2016, 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,15 +27,18 @@
  * @summary Need to strip leading zeros in TlsPremasterSecret of DHKeyAgreement
  * @library ..
  * @author Pasi Eronen
+ * @run main/othervm TestLeadingZeroesP11
+ * @run main/othervm TestLeadingZeroesP11 sm
  */
 
-import java.io.*;
-import java.security.*;
-import java.security.spec.*;
-import java.security.interfaces.*;
-import javax.crypto.*;
-import javax.crypto.spec.*;
-import javax.crypto.interfaces.*;
+
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import javax.crypto.KeyAgreement;
 
 /**
  * Test that leading zeroes are stripped in TlsPremasterSecret case,
@@ -48,9 +51,10 @@ import javax.crypto.interfaces.*;
 public class TestLeadingZeroesP11 extends PKCS11Test {
 
     public static void main(String[] args) throws Exception {
-        main(new TestLeadingZeroesP11());
+        main(new TestLeadingZeroesP11(), args);
     }
 
+    @Override
     public void main(Provider p) throws Exception {
 
         // decode pre-generated keypairs
diff --git a/jdk/test/sun/security/pkcs11/tls/TestMasterSecret.java b/jdk/test/sun/security/pkcs11/tls/TestMasterSecret.java
index fb00bd7fbf0..855b8c21f13 100644
--- a/jdk/test/sun/security/pkcs11/tls/TestMasterSecret.java
+++ b/jdk/test/sun/security/pkcs11/tls/TestMasterSecret.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2016, 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
@@ -29,111 +29,112 @@
  * @library ..
  * @modules java.base/sun.security.internal.interfaces
  *          java.base/sun.security.internal.spec
+ * @run main/othervm TestMasterSecret
+ * @run main/othervm TestMasterSecret sm TestMasterSecret.policy
  */
 
-import java.io.*;
-import java.util.*;
-
-import java.security.Security;
+import java.io.BufferedReader;
+import java.nio.file.Files;
+import java.nio.file.Paths;
 import java.security.Provider;
-
+import java.util.Arrays;
 import javax.crypto.KeyGenerator;
 import javax.crypto.SecretKey;
-
-import javax.crypto.spec.*;
-
-import sun.security.internal.spec.*;
+import javax.crypto.spec.SecretKeySpec;
 import sun.security.internal.interfaces.TlsMasterSecret;
+import sun.security.internal.spec.TlsMasterSecretParameterSpec;
 
 public class TestMasterSecret extends PKCS11Test {
 
-    private static int PREFIX_LENGTH = "m-premaster:  ".length();
+    private static final int PREFIX_LENGTH = "m-premaster:  ".length();
 
     public static void main(String[] args) throws Exception {
-        main(new TestMasterSecret());
+        main(new TestMasterSecret(), args);
     }
 
+    @Override
     public void main(Provider provider) throws Exception {
         if (provider.getService("KeyGenerator", "SunTlsMasterSecret") == null) {
             System.out.println("Not supported by provider, skipping");
             return;
         }
-        InputStream in = new FileInputStream(new File(BASE, "masterdata.txt"));
-        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
 
-        int n = 0;
-        int lineNumber = 0;
+        try (BufferedReader reader = Files.newBufferedReader(
+                Paths.get(BASE, "masterdata.txt"))) {
 
-        String algorithm = null;
-        byte[] premaster = null;
-        byte[] clientRandom = null;
-        byte[] serverRandom = null;
-        int protoMajor = 0;
-        int protoMinor = 0;
-        int preMajor = 0;
-        int preMinor = 0;
-        byte[] master = null;
+            int n = 0;
+            int lineNumber = 0;
 
-        while (true) {
-            String line = reader.readLine();
-            lineNumber++;
-            if (line == null) {
-                break;
-            }
-            if (line.startsWith("m-") == false) {
-                continue;
-            }
-            String data = line.substring(PREFIX_LENGTH);
-            if (line.startsWith("m-algorithm:")) {
-                algorithm = data;
-            } else if (line.startsWith("m-premaster:")) {
-                premaster = parse(data);
-            } else if (line.startsWith("m-crandom:")) {
-                clientRandom = parse(data);
-            } else if (line.startsWith("m-srandom:")) {
-                serverRandom = parse(data);
-            } else if (line.startsWith("m-protomajor:")) {
-                protoMajor = Integer.parseInt(data);
-            } else if (line.startsWith("m-protominor:")) {
-                protoMinor = Integer.parseInt(data);
-            } else if (line.startsWith("m-premajor:")) {
-                preMajor = Integer.parseInt(data);
-            } else if (line.startsWith("m-preminor:")) {
-                preMinor = Integer.parseInt(data);
-            } else if (line.startsWith("m-master:")) {
-                master = parse(data);
+            String algorithm = null;
+            byte[] premaster = null;
+            byte[] clientRandom = null;
+            byte[] serverRandom = null;
+            int protoMajor = 0;
+            int protoMinor = 0;
+            int preMajor = 0;
+            int preMinor = 0;
+            byte[] master = null;
 
-                System.out.print(".");
-                n++;
-
-                KeyGenerator kg =
-                    KeyGenerator.getInstance("SunTlsMasterSecret", provider);
-                SecretKey premasterKey =
-                    new SecretKeySpec(premaster, algorithm);
-                TlsMasterSecretParameterSpec spec =
-                    new TlsMasterSecretParameterSpec(premasterKey,
-                        protoMajor, protoMinor, clientRandom, serverRandom,
-                        null, -1, -1);
-                kg.init(spec);
-                TlsMasterSecret key = (TlsMasterSecret)kg.generateKey();
-                byte[] enc = key.getEncoded();
-                if (Arrays.equals(master, enc) == false) {
-                    throw new Exception("mismatch line: " + lineNumber);
+            while (true) {
+                String line = reader.readLine();
+                lineNumber++;
+                if (line == null) {
+                    break;
                 }
-                if ((preMajor != key.getMajorVersion()) ||
-                        (preMinor != key.getMinorVersion())) {
-                    throw new Exception("version mismatch line: " + lineNumber);
+                if (line.startsWith("m-") == false) {
+                    continue;
+                }
+                String data = line.substring(PREFIX_LENGTH);
+                if (line.startsWith("m-algorithm:")) {
+                    algorithm = data;
+                } else if (line.startsWith("m-premaster:")) {
+                    premaster = parse(data);
+                } else if (line.startsWith("m-crandom:")) {
+                    clientRandom = parse(data);
+                } else if (line.startsWith("m-srandom:")) {
+                    serverRandom = parse(data);
+                } else if (line.startsWith("m-protomajor:")) {
+                    protoMajor = Integer.parseInt(data);
+                } else if (line.startsWith("m-protominor:")) {
+                    protoMinor = Integer.parseInt(data);
+                } else if (line.startsWith("m-premajor:")) {
+                    preMajor = Integer.parseInt(data);
+                } else if (line.startsWith("m-preminor:")) {
+                    preMinor = Integer.parseInt(data);
+                } else if (line.startsWith("m-master:")) {
+                    master = parse(data);
+
+                    System.out.print(".");
+                    n++;
+
+                    KeyGenerator kg =
+                        KeyGenerator.getInstance("SunTlsMasterSecret", provider);
+                    SecretKey premasterKey =
+                        new SecretKeySpec(premaster, algorithm);
+                    TlsMasterSecretParameterSpec spec =
+                        new TlsMasterSecretParameterSpec(premasterKey,
+                            protoMajor, protoMinor, clientRandom, serverRandom,
+                            null, -1, -1);
+                    kg.init(spec);
+                    TlsMasterSecret key = (TlsMasterSecret)kg.generateKey();
+                    byte[] enc = key.getEncoded();
+                    if (Arrays.equals(master, enc) == false) {
+                        throw new Exception("mismatch line: " + lineNumber);
+                    }
+                    if ((preMajor != key.getMajorVersion()) ||
+                            (preMinor != key.getMinorVersion())) {
+                        throw new Exception("version mismatch line: " + lineNumber);
+                    }
+                } else {
+                    throw new Exception("Unknown line: " + line);
                 }
-            } else {
-                throw new Exception("Unknown line: " + line);
             }
+            if (n == 0) {
+                throw new Exception("no tests");
+            }
+            System.out.println();
+            System.out.println("OK: " + n + " tests");
         }
-        if (n == 0) {
-            throw new Exception("no tests");
-        }
-        in.close();
-        System.out.println();
-        System.out.println("OK: " + n + " tests");
     }
 
 }
diff --git a/jdk/test/sun/security/pkcs11/tls/TestMasterSecret.policy b/jdk/test/sun/security/pkcs11/tls/TestMasterSecret.policy
new file mode 100644
index 00000000000..4b98541ad7a
--- /dev/null
+++ b/jdk/test/sun/security/pkcs11/tls/TestMasterSecret.policy
@@ -0,0 +1,8 @@
+grant {
+    permission java.lang.RuntimePermission "setSecurityManager";
+    permission java.io.FilePermission "${test.src}/*", "read";
+    permission java.lang.RuntimePermission
+        "accessClassInPackage.sun.security.internal.spec";
+    permission java.lang.RuntimePermission
+        "accessClassInPackage.sun.security.internal.interfaces";
+};
\ No newline at end of file
diff --git a/jdk/test/sun/security/pkcs11/tls/TestPRF.java b/jdk/test/sun/security/pkcs11/tls/TestPRF.java
index eefffe70820..6e05ea64f90 100644
--- a/jdk/test/sun/security/pkcs11/tls/TestPRF.java
+++ b/jdk/test/sun/security/pkcs11/tls/TestPRF.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2016, 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
@@ -28,116 +28,116 @@
  * @author Andreas Sterbenz
  * @library ..
  * @modules java.base/sun.security.internal.spec
+ * @run main/othervm TestPRF
+ * @run main/othervm TestPRF sm policy
  */
 
-import java.io.*;
-import java.util.*;
-
-import java.security.Security;
+import java.io.BufferedReader;
+import java.nio.file.Files;
+import java.nio.file.Paths;
 import java.security.Provider;
-
+import java.util.Arrays;
 import javax.crypto.KeyGenerator;
 import javax.crypto.SecretKey;
-
-import javax.crypto.spec.*;
-
-import sun.security.internal.spec.*;
+import javax.crypto.spec.SecretKeySpec;
+import sun.security.internal.spec.TlsPrfParameterSpec;
 
 public class TestPRF extends PKCS11Test {
 
-    private static int PREFIX_LENGTH = "prf-output: ".length();
+    private static final int PREFIX_LENGTH = "prf-output: ".length();
 
     public static void main(String[] args) throws Exception {
-        main(new TestPRF());
+        main(new TestPRF(), args);
     }
 
+    @Override
     public void main(Provider provider) throws Exception {
         if (provider.getService("KeyGenerator", "SunTlsPrf") == null) {
             System.out.println("Provider does not support algorithm, skipping");
             return;
         }
 
-        InputStream in = new FileInputStream(new File(BASE, "prfdata.txt"));
-        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+        try (BufferedReader reader = Files.newBufferedReader(
+                Paths.get(BASE, "prfdata.txt"))) {
 
-        int n = 0;
-        int lineNumber = 0;
+            int n = 0;
+            int lineNumber = 0;
 
-        byte[] secret = null;
-        String label = null;
-        byte[] seed = null;
-        int length = 0;
-        byte[] output = null;
+            byte[] secret = null;
+            String label = null;
+            byte[] seed = null;
+            int length = 0;
+            byte[] output = null;
 
-        while (true) {
-            String line = reader.readLine();
-            lineNumber++;
-            if (line == null) {
-                break;
-            }
-            if (line.startsWith("prf-") == false) {
-                continue;
-            }
-
-            String data = line.substring(PREFIX_LENGTH);
-            if (line.startsWith("prf-secret:")) {
-                secret = parse(data);
-            } else if (line.startsWith("prf-label:")) {
-                label = data;
-            } else if (line.startsWith("prf-seed:")) {
-                seed = parse(data);
-            } else if (line.startsWith("prf-length:")) {
-                length = Integer.parseInt(data);
-            } else if (line.startsWith("prf-output:")) {
-                output = parse(data);
-
-                System.out.print(".");
-                n++;
-
-                KeyGenerator kg =
-                    KeyGenerator.getInstance("SunTlsPrf", provider);
-                SecretKey inKey;
-                if (secret == null) {
-                    inKey = null;
-                } else {
-                    inKey = new SecretKeySpec(secret, "Generic");
+            while (true) {
+                String line = reader.readLine();
+                lineNumber++;
+                if (line == null) {
+                    break;
                 }
-                TlsPrfParameterSpec spec =
-                    new TlsPrfParameterSpec(inKey, label, seed, length,
-                        null, -1, -1);
-                SecretKey key;
-                try {
-                    kg.init(spec);
-                    key = kg.generateKey();
-                } catch (Exception e) {
+                if (line.startsWith("prf-") == false) {
+                    continue;
+                }
+
+                String data = line.substring(PREFIX_LENGTH);
+                if (line.startsWith("prf-secret:")) {
+                    secret = parse(data);
+                } else if (line.startsWith("prf-label:")) {
+                    label = data;
+                } else if (line.startsWith("prf-seed:")) {
+                    seed = parse(data);
+                } else if (line.startsWith("prf-length:")) {
+                    length = Integer.parseInt(data);
+                } else if (line.startsWith("prf-output:")) {
+                    output = parse(data);
+
+                    System.out.print(".");
+                    n++;
+
+                    KeyGenerator kg =
+                        KeyGenerator.getInstance("SunTlsPrf", provider);
+                    SecretKey inKey;
                     if (secret == null) {
-                        // This fails on Solaris, but since we never call this
-                        // API for this case in JSSE, ignore the failure.
-                        // (SunJSSE uses the CKM_TLS_KEY_AND_MAC_DERIVE
-                        // mechanism)
-                        System.out.print("X");
-                        continue;
+                        inKey = null;
+                    } else {
+                        inKey = new SecretKeySpec(secret, "Generic");
                     }
-                    System.out.println();
-                    throw new Exception("Error on line: " + lineNumber, e);
+                    TlsPrfParameterSpec spec =
+                        new TlsPrfParameterSpec(inKey, label, seed, length,
+                            null, -1, -1);
+                    SecretKey key;
+                    try {
+                        kg.init(spec);
+                        key = kg.generateKey();
+                    } catch (Exception e) {
+                        if (secret == null) {
+                            // This fails on Solaris, but since we never call this
+                            // API for this case in JSSE, ignore the failure.
+                            // (SunJSSE uses the CKM_TLS_KEY_AND_MAC_DERIVE
+                            // mechanism)
+                            System.out.print("X");
+                            continue;
+                        }
+                        System.out.println();
+                        throw new Exception("Error on line: " + lineNumber, e);
+                    }
+                    byte[] enc = key.getEncoded();
+                    if (Arrays.equals(output, enc) == false) {
+                        System.out.println();
+                        System.out.println("expected: " + toString(output));
+                        System.out.println("actual:   " + toString(enc));
+                        throw new Exception("mismatch line: " + lineNumber);
+                    }
+                } else {
+                    throw new Exception("Unknown line: " + line);
                 }
-                byte[] enc = key.getEncoded();
-                if (Arrays.equals(output, enc) == false) {
-                    System.out.println();
-                    System.out.println("expected: " + toString(output));
-                    System.out.println("actual:   " + toString(enc));
-                    throw new Exception("mismatch line: " + lineNumber);
-                }
-            } else {
-                throw new Exception("Unknown line: " + line);
             }
+            if (n == 0) {
+                throw new Exception("no tests");
+            }
+            System.out.println();
+            System.out.println("OK: " + n + " tests");
         }
-        if (n == 0) {
-            throw new Exception("no tests");
-        }
-        in.close();
-        System.out.println();
-        System.out.println("OK: " + n + " tests");
     }
 
 }
diff --git a/jdk/test/sun/security/pkcs11/tls/TestPremaster.java b/jdk/test/sun/security/pkcs11/tls/TestPremaster.java
index 15b13ac8507..bbbbf2c376d 100644
--- a/jdk/test/sun/security/pkcs11/tls/TestPremaster.java
+++ b/jdk/test/sun/security/pkcs11/tls/TestPremaster.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2016, 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
@@ -28,23 +28,22 @@
  * @author Andreas Sterbenz
  * @library ..
  * @modules java.base/sun.security.internal.spec
+ * @run main/othervm TestPremaster
+ * @run main/othervm TestPremaster sm policy
  */
 
-import java.security.Security;
 import java.security.Provider;
-
 import javax.crypto.KeyGenerator;
 import javax.crypto.SecretKey;
-import java.util.Formatter;
-
 import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec;
 
 public class TestPremaster extends PKCS11Test {
 
     public static void main(String[] args) throws Exception {
-        main(new TestPremaster());
+        main(new TestPremaster(), args);
     }
 
+    @Override
     public void main(Provider provider) throws Exception {
         if (provider.getService(
                 "KeyGenerator", "SunTlsRsaPremasterSecret") == null) {
diff --git a/jdk/test/sun/security/pkcs11/tls/policy b/jdk/test/sun/security/pkcs11/tls/policy
new file mode 100644
index 00000000000..6d161b9b2e5
--- /dev/null
+++ b/jdk/test/sun/security/pkcs11/tls/policy
@@ -0,0 +1,5 @@
+grant {
+    permission java.lang.RuntimePermission "setSecurityManager";
+    permission java.io.FilePermission "${test.src}/*", "read";
+    permission java.lang.RuntimePermission "accessClassInPackage.sun.security.internal.spec";
+};
\ No newline at end of file
diff --git a/make/CompileJavaModules.gmk b/make/CompileJavaModules.gmk
index 06241b35ba9..8695cb9a393 100644
--- a/make/CompileJavaModules.gmk
+++ b/make/CompileJavaModules.gmk
@@ -42,7 +42,7 @@ java.activation_SETUP := GENERATE_JDKBYTECODE_NOWARNINGS
 
 ################################################################################
 
-java.base_ADD_JAVAC_FLAGS := -Xdoclint:all/protected,-reference '-Xdoclint/package:java.*,javax.*'
+java.base_ADD_JAVAC_FLAGS := -Xdoclint:all/protected,-reference '-Xdoclint/package:java.*,javax.*' -XDstringConcat=inline
 java.base_COPY := .icu .dat .spp content-types.properties hijrah-config-islamic-umalqura.properties
 java.base_CLEAN := intrinsic.properties
 
@@ -359,7 +359,7 @@ jdk.charsets_COPY := .dat
 
 ################################################################################
 
-jdk.compiler_ADD_JAVAC_FLAGS := -Xdoclint:all/protected '-Xdoclint/package:-com.sun.tools.*'
+jdk.compiler_ADD_JAVAC_FLAGS := -Xdoclint:all/protected '-Xdoclint/package:-com.sun.tools.*' -XDstringConcat=inline
 jdk.compiler_COPY := javax.tools.JavaCompilerTool
 jdk.compiler_CLEAN_FILES := $(wildcard \
     $(patsubst %, $(JDK_TOPDIR)/src/jdk.compiler/share/classes/%/*.properties, \
diff --git a/make/Init.gmk b/make/Init.gmk
index a231526db05..b0c2d881bf4 100644
--- a/make/Init.gmk
+++ b/make/Init.gmk
@@ -315,6 +315,7 @@ else # HAS_SPEC=true
 
     # Support targets for COMPARE_BUILD, used for makefile development
     pre-compare-build:
+	$(call WaitForSmartJavacFinish)
 	$(call PrepareCompareBuild)
 
     post-compare-build:
diff --git a/make/InitSupport.gmk b/make/InitSupport.gmk
index 6e10c7b59e3..2bf05d1eb29 100644
--- a/make/InitSupport.gmk
+++ b/make/InitSupport.gmk
@@ -440,7 +440,10 @@ else # $(HAS_SPEC)=true
 	  $(PRINTF) "=== Output from failing command(s) repeated here ===\n" $(NEWLINE) \
 	  $(foreach logfile, $(sort $(wildcard $(MAKESUPPORT_OUTPUTDIR)/failure-logs/*)), \
 	      $(PRINTF) "* For target $(notdir $(basename $(logfile))):\n" $(NEWLINE) \
-	      $(CAT) $(logfile) | $(GREP) -v -e "^Note: including file:" $(NEWLINE) \
+	      ($(GREP) -v -e "^Note: including file:" <  $(logfile) || true) | $(HEAD) -n 12 $(NEWLINE) \
+	      if test `$(WC) -l < $(logfile)` -gt 12; then \
+	        $(ECHO) "   ... (rest of output omitted)" ; \
+	      fi $(NEWLINE) \
 	  ) \
 	  $(PRINTF) "=== End of repeated output ===\n" \
 	)
@@ -486,6 +489,21 @@ else # $(HAS_SPEC)=true
 	    $(TOUCH) $(SJAVAC_SERVER_DIR)/server.port.stop; true
   endef
 
+  ifeq ($(OPENJDK_BUILD_OS), windows)
+    # On windows we need to synchronize with the javac server to be able to
+    # move or remove the build output directory. Since we have no proper
+    # synchronization process, wait for a while and hope it helps. This is only
+    # used by build comparisons.
+    define WaitForSmartJavacFinish
+	$(if $(SJAVAC_SERVER_DIR), \
+	  sleep 5\
+	)
+    endef
+  else
+    define WaitForSmartJavacFinish
+    endef
+  endif
+
   define StartGlobalTimer
 	$(RM) -r $(BUILDTIMESDIR) 2> /dev/null && \
 	$(MKDIR) -p $(BUILDTIMESDIR) && \
diff --git a/make/Javadoc.gmk b/make/Javadoc.gmk
index 01c0a6fcbf2..05093f4612a 100644
--- a/make/Javadoc.gmk
+++ b/make/Javadoc.gmk
@@ -76,6 +76,7 @@ SCTPAPI_FIRST_COPYRIGHT_YEAR = 2009
 TRACING_FIRST_COPYRIGHT_YEAR = 2008
 TREEAPI_FIRST_COPYRIGHT_YEAR = 2005
 NASHORNAPI_FIRST_COPYRIGHT_YEAR = 2014
+DYNALINKAPI_FIRST_COPYRIGHT_YEAR = 2015
 JNLP_FIRST_COPYRIGHT_YEAR = 1998
 PLUGIN2_FIRST_COPYRIGHT_YEAR = 2007
 JDKNET_FIRST_COPYRIGHT_YEAR = 2014
@@ -418,7 +419,7 @@ DOCLETAPI_WINDOWTITLE := Doclet API
 DOCLETAPI_HEADER := <strong>Doclet API</strong>
 DOCLETAPI_BOTTOM := $(call CommonTrademarkBottom,$(DOCLETAPI_FIRST_COPYRIGHT_YEAR))
 DOCLETAPI_GROUPNAME := Packages
-DOCLETAPI_REGEXP := com.sun.javadoc
+DOCLETAPI_REGEXP := jdk.javadoc.doclet.*
 # DOCLETAPI_PKGS is located in NON_CORE_PKGS.gmk
 
 # The index.html, options, and packages files
@@ -460,14 +461,71 @@ $(DOCLETAPI_PACKAGES_FILE): $(call PackageDependencies,$(DOCLETAPI_PKGS))
 	$(prep-target)
 	$(call PackageFilter,$(DOCLETAPI_PKGS))
 
+#############################################################
+#
+# old docletapidocs
+#
+
+ALL_OTHER_TARGETS += olddocletapidocs
+
+OLD_DOCLET_DIR := $(JDK_API_DOCSDIR)/javadoc/old
+OLD_DOCLETAPI_DOCDIR := $(OLD_DOCLET_DIR)/doclet
+OLD_DOCLETAPI2COREAPI := ../../../$(JDKJRE2COREAPI)
+OLD_DOCLETAPI_DOCTITLE := Doclet API
+OLD_DOCLETAPI_WINDOWTITLE := Doclet API
+OLD_DOCLETAPI_HEADER := <strong>Doclet API</strong>
+OLD_DOCLETAPI_BOTTOM := $(call CommonTrademarkBottom,$(DOCLETAPI_FIRST_COPYRIGHT_YEAR))
+OLD_DOCLETAPI_GROUPNAME := Packages
+OLD_DOCLETAPI_REGEXP := com.sun.javadoc
+# OLD_DOCLETAPI_PKGS is located in NON_CORE_PKGS.gmk
+
+# The index.html, options, and packages files
+OLD_DOCLETAPI_INDEX_FILE = $(OLD_DOCLETAPI_DOCDIR)/index.html
+OLD_DOCLETAPI_OPTIONS_FILE = $(DOCSTMPDIR)/old-docletapi.options
+OLD_DOCLETAPI_PACKAGES_FILE = $(DOCSTMPDIR)/old-docletapi.packages
+
+olddocletapidocs: $(OLD_DOCLETAPI_INDEX_FILE)
+
+# Set relative location to core api document root
+$(OLD_DOCLETAPI_INDEX_FILE): GET2DOCSDIR=$(OLD_DOCLETAPI2COREAPI)/..
+
+# Run javadoc if the index file is out of date or missing
+$(OLD_DOCLETAPI_INDEX_FILE): $(OLD_DOCLETAPI_OPTIONS_FILE) $(OLD_DOCLETAPI_PACKAGES_FILE) $(COREAPI_INDEX_FILE)
+	$(prep-javadoc)
+	$(call JavadocSummary,$(OLD_DOCLETAPI_OPTIONS_FILE),$(OLD_DOCLETAPI_PACKAGES_FILE))
+	$(JAVADOC_CMD_SMALL) -d $(@D) \
+	    @$(OLD_DOCLETAPI_OPTIONS_FILE) @$(OLD_DOCLETAPI_PACKAGES_FILE)
+
+# Create file with javadoc options in it
+$(OLD_DOCLETAPI_OPTIONS_FILE):
+	$(prep-target)
+	@($(call COMMON_JAVADOCFLAGS) ; \
+          $(call COMMON_JAVADOCTAGS) ; \
+	  $(call OptionOnly,-Xdoclint:all) ; \
+	  $(call OptionPair,-sourcepath,$(RELEASEDOCS_SOURCEPATH)) ; \
+	  $(call OptionPair,-encoding,ascii) ; \
+	  $(call OptionOnly,-breakiterator) ; \
+	  $(call OptionPair,-doctitle,$(OLD_DOCLETAPI_DOCTITLE)) ; \
+	  $(call OptionPair,-windowtitle,$(OLD_DOCLETAPI_WINDOWTITLE) $(DRAFT_WINTITLE)); \
+	  $(call OptionPair,-header,$(OLD_DOCLETAPI_HEADER)$(DRAFT_HEADER)) ; \
+	  $(call OptionPair,-bottom,$(OLD_DOCLETAPI_BOTTOM)$(DRAFT_BOTTOM)) ; \
+	  $(call OptionTrip,-group,$(OLD_DOCLETAPI_GROUPNAME),$(OLD_DOCLETAPI_REGEXP)); \
+	  $(call OptionTrip,-linkoffline,$(OLD_DOCLETAPI2COREAPI),$(COREAPI_DOCSDIR)/); \
+	) >> $@
+
+# Create a file with the package names in it
+$(OLD_DOCLETAPI_PACKAGES_FILE): $(call PackageDependencies,$(OLD_DOCLETAPI_PKGS))
+	$(prep-target)
+	$(call PackageFilter,$(OLD_DOCLETAPI_PKGS))
+
 #############################################################
 #
 # tagletapidocs
 #
 
 ALL_OTHER_TARGETS += tagletapidocs
-TAGLETAPI_DOCDIR := $(JDK_API_DOCSDIR)/javadoc/taglet
-TAGLETAPI2COREAPI := ../../$(JDKJRE2COREAPI)
+TAGLETAPI_DOCDIR := $(OLD_DOCLET_DIR)/taglet
+TAGLETAPI2COREAPI := ../../../$(JDKJRE2COREAPI)
 TAGLETAPI_BOTTOM := $(call CommonTrademarkBottom,$(TAGLETAPI_FIRST_COPYRIGHT_YEAR))
 # TAGLETAPI_FILE is located in NON_CORE_PKGS.gmk
 
@@ -1156,6 +1214,60 @@ $(NASHORNAPI_PACKAGES_FILE): $(call PackageDependencies,$(NASHORNAPI_PKGS))
 	$(prep-target)
 	$(call PackageFilter,$(NASHORNAPI_PKGS))
 
+#############################################################
+#
+# dynalinkapidocs
+#
+
+ALL_OTHER_TARGETS += dynalinkapidocs
+
+DYNALINKAPI_DOCDIR := $(JDK_API_DOCSDIR)/dynalink
+DYNALINKAPI2COREAPI := ../$(JDKJRE2COREAPI)
+DYNALINKAPI_DOCTITLE := Dynalink API
+DYNALINKAPI_WINDOWTITLE := Dynalink API
+DYNALINKAPI_HEADER := <strong>Dynalink API</strong>
+DYNALINKAPI_BOTTOM := $(call CommonBottom,$(DYNALINKAPI_FIRST_COPYRIGHT_YEAR))
+DYNALINKAPI_GROUPNAME := Packages
+DYNALINKAPI_REGEXP := jdk.dynalink.*
+# DYNALINKAPI_PKGS is located in NON_CORE_PKGS.gmk
+
+DYNALINKAPI_INDEX_HTML = $(DYNALINKAPI_DOCDIR)/index.html
+DYNALINKAPI_OPTIONS_FILE = $(DOCSTMPDIR)/dynalinkapi.options
+DYNALINKAPI_PACKAGES_FILE = $(DOCSTMPDIR)/dynalinkapi.packages
+
+dynalinkapidocs: $(DYNALINKAPI_INDEX_HTML)
+
+# Set relative location to core api document root
+$(DYNALINKAPI_INDEX_HTML): GET2DOCSDIR=$(DYNALINKAPI2COREAPI)/..
+
+# Run javadoc if the index file is out of date or missing
+$(DYNALINKAPI_INDEX_HTML): $(DYNALINKAPI_OPTIONS_FILE) $(DYNALINKAPI_PACKAGES_FILE) $(COREAPI_INDEX_FILE)
+	$(prep-javadoc)
+	$(call JavadocSummary,$(DYNALINKAPI_OPTIONS_FILE),$(DYNALINKAPI_PACKAGES_FILE))
+	$(JAVADOC_CMD_SMALL) -d $(@D) \
+	    @$(DYNALINKAPI_OPTIONS_FILE) @$(DYNALINKAPI_PACKAGES_FILE)
+
+# Create file with javadoc options in it
+$(DYNALINKAPI_OPTIONS_FILE):
+	$(prep-target)
+	@($(call COMMON_JAVADOCFLAGS) ; \
+          $(call COMMON_JAVADOCTAGS) ; \
+	  $(call OptionOnly,-Xdoclint:all) ; \
+	  $(call OptionPair,-sourcepath,$(RELEASEDOCS_SOURCEPATH)) ; \
+	  $(call OptionPair,-encoding,ascii) ; \
+	  $(call OptionPair,-doctitle,$(DYNALINKAPI_DOCTITLE)) ; \
+	  $(call OptionPair,-windowtitle,$(DYNALINKAPI_WINDOWTITLE) $(DRAFT_WINTITLE)); \
+	  $(call OptionPair,-header,$(DYNALINKAPI_HEADER)$(DRAFT_HEADER)) ; \
+	  $(call OptionPair,-bottom,$(DYNALINKAPI_BOTTOM)$(DRAFT_BOTTOM)) ; \
+	  $(call OptionTrip,-group,$(DYNALINKAPI_GROUPNAME),$(DYNALINKAPI_REGEXP)); \
+	  $(call OptionTrip,-linkoffline,$(DYNALINKAPI2COREAPI),$(COREAPI_DOCSDIR)/); \
+	) >> $@
+
+# Create a file with the package names in it
+$(DYNALINKAPI_PACKAGES_FILE): $(call PackageDependencies,$(DYNALINKAPI_PKGS))
+	$(prep-target)
+	$(call PackageFilter,$(DYNALINKAPI_PKGS))
+
 #############################################################
 #
 # sctpdocs
diff --git a/make/common/JavaCompilation.gmk b/make/common/JavaCompilation.gmk
index b1c6d37c1a8..8b4eb594f83 100644
--- a/make/common/JavaCompilation.gmk
+++ b/make/common/JavaCompilation.gmk
@@ -345,7 +345,7 @@ define SetupJavaCompilationBody
 	  $$(eval $$(call ListPathsSafely,$1_SJAVAC_ARGS_STRING, $$($1_SJAVAC_ARGS_FILE)))
         endif
 	$(ECHO) Compiling $1
-	$(call LogFailures, $$($1_BIN)/_the.$$($1_SAFE_NAME)_batch.log, $$($1_SAFE_NAME), \
+	$(call LogFailures, $$($1_BIN)/_the.$$($1_SAFE_NAME)_batch, $$($1_SAFE_NAME), \
 	    $$($1_JVM) $$($1_SJAVAC) \
 	        $$($1_REMOTE) \
 	        -j 1 \
@@ -409,7 +409,7 @@ define SetupJavaCompilationBody
 	$(MKDIR) -p $$(@D)
 	$$(eval $$(call ListPathsSafely,$1_SRCS, $$@.tmp))
 	$(ECHO) Compiling `$(WC) $$@.tmp | $(TR) -s ' ' | $(CUT) -f 2 -d ' '` files for $1
-	$(call LogFailures, $$($1_BIN)/_the.$$($1_SAFE_NAME)_batch.log, $$($1_SAFE_NAME), \
+	$(call LogFailures, $$($1_BIN)/_the.$$($1_SAFE_NAME)_batch, $$($1_SAFE_NAME), \
 	    $$($1_JVM) $$($1_JAVAC_CMD) $$($1_FLAGS) \
 	        -implicit:none \
 		-d $$($1_BIN) $$($1_HEADERS_ARG) @$$@.tmp) && \
diff --git a/make/common/MakeBase.gmk b/make/common/MakeBase.gmk
index 1f3bdb76650..363168d27a8 100644
--- a/make/common/MakeBase.gmk
+++ b/make/common/MakeBase.gmk
@@ -685,14 +685,15 @@ DependOnVariable = \
 # LogFailures will run a command and store a copy of output in a specified file.
 # If the command succeeds, the file is deleted, otherwise it is moved to the
 # failure-logs directory.
-# Param 1 - The log file of the failed command
+# Param 1 - The base name of the log file / command line file
 # Param 2 - A compact but representative name to describe this command
 # Param 3 - Command to run
 LogFailures = \
-  ( $3 > >($(TEE) $1) 2> >($(TEE) $1 >&2) || \
+  ( $(ECHO) '$3' > $1.cmdline  && \
+      ( $3 > >($(TEE) $1.log) 2> >($(TEE) $1.log >&2) || \
       (exitcode=$(DOLLAR)$(DOLLAR)? && \
-      $(CP) $1 $(MAKESUPPORT_OUTPUTDIR)/failure-logs/$(strip $2).log && \
-      exit $(DOLLAR)$(DOLLAR)exitcode) )
+      $(CP) $1.log $(MAKESUPPORT_OUTPUTDIR)/failure-logs/$(strip $2).log && \
+      exit $(DOLLAR)$(DOLLAR)exitcode) ) )
 
 ################################################################################
 # Find lib dir for module
diff --git a/make/common/NON_CORE_PKGS.gmk b/make/common/NON_CORE_PKGS.gmk
index b6d92120b08..bf02064cf63 100644
--- a/make/common/NON_CORE_PKGS.gmk
+++ b/make/common/NON_CORE_PKGS.gmk
@@ -66,7 +66,10 @@ HTTPSERVER_PKGS = com.sun.net.httpserver \
 
 NIO_PKGS = com.sun.nio.file
 
-DOCLETAPI_PKGS = com.sun.javadoc
+OLD_DOCLETAPI_PKGS = com.sun.javadoc
+
+DOCLETAPI_PKGS = jdk.javadoc.doclet \
+    jdk.javadoc.doclet.taglet
 
 TAGLETAPI_FILE = com/sun/tools/doclets/Taglet.java
 
@@ -83,6 +86,12 @@ TREEAPI_PKGS = com.sun.source.doctree \
 
 NASHORNAPI_PKGS = jdk.nashorn.api.scripting \
     jdk.nashorn.api.tree
+   
+DYNALINKAPI_PKGS = jdk.dynalink \
+    jdk.dynalink.beans \
+    jdk.dynalink.linker \
+    jdk.dynalink.linker.support \
+    jdk.dynalink.support
 
 SMARTCARDIO_PKGS = javax.smartcardio
 
diff --git a/make/common/NativeCompilation.gmk b/make/common/NativeCompilation.gmk
index b080beac1dc..e3fa6fd2100 100644
--- a/make/common/NativeCompilation.gmk
+++ b/make/common/NativeCompilation.gmk
@@ -241,15 +241,16 @@ define add_native_source
 
     $$($1_$2_OBJ) : $2 $$($1_COMPILE_VARDEPS_FILE) $$($1_$2_VARDEPS_FILE) | $$($1_BUILD_INFO)
 	$$(call LogInfo, Compiling $$(notdir $2) (for $$(notdir $$($1_TARGET))))
+	$$(call MakeDir, $$(@D))
         ifneq ($(TOOLCHAIN_TYPE), microsoft)
           ifeq ($(TOOLCHAIN_TYPE)$$(filter %.s,$2), solstudio)
             # The Solaris studio compiler doesn't output the full path to the object file in the
             # generated deps files. Fixing it with sed. If compiling assembly, don't try this.
-	    $(call LogFailures, $$($1_$2_OBJ).log, $$($1_SAFE_NAME)_$$(notdir $2), \
+	    $(call LogFailures, $$($1_$2_OBJ), $$($1_SAFE_NAME)_$$(notdir $2), \
 	        $$($1_$2_COMP) $$($1_$2_FLAGS) $$($1_$2_DEP_FLAG) $$($1_$2_DEP).tmp $(CC_OUT_OPTION)$$($1_$2_OBJ) $2)
 	    $(SED) 's|^$$(@F):|$$@:|' $$($1_$2_DEP).tmp > $$($1_$2_DEP)
           else
-	    $(call LogFailures, $$($1_$2_OBJ).log, $$($1_SAFE_NAME)_$$(notdir $2), \
+	    $(call LogFailures, $$($1_$2_OBJ), $$($1_SAFE_NAME)_$$(notdir $2), \
 	        $$($1_$2_COMP) $$($1_$2_FLAGS) $$($1_$2_DEP_FLAG) $$($1_$2_DEP) $(CC_OUT_OPTION)$$($1_$2_OBJ) $2)
           endif
           # Create a dependency target file from the dependency file.
@@ -264,7 +265,7 @@ define add_native_source
           # Keep as much as possible on one execution line for best performance on Windows.
           # No need to save exit code from compilation since pipefail is always active on
           # Windows.
-	  $(call LogFailures, $$($1_$2_OBJ).log, $$($1_SAFE_NAME)_$$(notdir $2), \
+	  $(call LogFailures, $$($1_$2_OBJ), $$($1_SAFE_NAME)_$$(notdir $2), \
 	      $$($1_$2_COMP) $$($1_$2_FLAGS) -showIncludes $$($1_$2_DEBUG_OUT_FLAGS) \
 	          $(CC_OUT_OPTION)$$($1_$2_OBJ) $2) \
 	      | $(GREP) -v -e "^Note: including file:" \
@@ -784,13 +785,23 @@ define SetupNativeCompilationBody
                 # Keep as much as possible on one execution line for best performance
                 # on Windows
 		$$(call LogInfo, Linking $$($1_BASENAME))
-		$(call LogFailures, $$($1_OBJECT_DIR)/$$($1_SAFE_NAME)_link.log, $$($1_SAFE_NAME)_link, \
-		    $$($1_LD) $$($1_LDFLAGS) $$($1_EXTRA_LDFLAGS) $$($1_SYSROOT_LDFLAGS) \
-		    $(LD_OUT_OPTION)$$@ \
-		    $$($1_LD_OBJ_ARG) $$($1_RES) \
-		    $$($1_LIBS) $$($1_EXTRA_LIBS)) ; \
-		$$($1_CREATE_DEBUGINFO_CMDS)
-		$$($1_STRIP_CMD)
+                ifeq ($(OPENJDK_TARGET_OS), windows)
+		  $(call LogFailures, $$($1_OBJECT_DIR)/$$($1_SAFE_NAME)_link, $$($1_SAFE_NAME)_link, \
+		      $$($1_LD) $$($1_LDFLAGS) $$($1_EXTRA_LDFLAGS) $$($1_SYSROOT_LDFLAGS) \
+		      $(LD_OUT_OPTION)$$@ $$($1_LD_OBJ_ARG) $$($1_RES) $$($1_LIBS) \
+		      $$($1_EXTRA_LIBS)) \
+		      | $(GREP) -v "^   Creating library .*\.lib and object .*\.exp" || \
+		      test "$$$$?" = "1" ; \
+		  $$($1_CREATE_DEBUGINFO_CMDS)
+		  $$($1_STRIP_CMD)
+                else
+		  $(call LogFailures, $$($1_OBJECT_DIR)/$$($1_SAFE_NAME)_link, $$($1_SAFE_NAME)_link, \
+		      $$($1_LD) $$($1_LDFLAGS) $$($1_EXTRA_LDFLAGS) $$($1_SYSROOT_LDFLAGS) \
+		      $(LD_OUT_OPTION)$$@ $$($1_LD_OBJ_ARG) $$($1_RES) $$($1_LIBS) \
+		      $$($1_EXTRA_LIBS)) ; \
+		  $$($1_CREATE_DEBUGINFO_CMDS)
+		  $$($1_STRIP_CMD)
+                endif
 
   endif
 
@@ -803,7 +814,7 @@ define SetupNativeCompilationBody
     # Generating a static library, ie object file archive.
     $$($1_TARGET): $$($1_ALL_OBJS) $$($1_RES) $$($1_VARDEPS_FILE)
 	$$(call LogInfo, Archiving $$($1_STATIC_LIBRARY))
-	$(call LogFailures, $$($1_OBJECT_DIR)/$$($1_SAFE_NAME)_link.log, $$($1_SAFE_NAME)_link, \
+	$(call LogFailures, $$($1_OBJECT_DIR)/$$($1_SAFE_NAME)_link, $$($1_SAFE_NAME)_link, \
 	    $$($1_AR) $$($1_ARFLAGS) $(AR_OUT_OPTION)$$($1_TARGET) $$($1_ALL_OBJS) \
 	        $$($1_RES))
         ifeq ($(STATIC_BUILD), true)
@@ -825,7 +836,7 @@ define SetupNativeCompilationBody
     $$($1_TARGET): $$($1_ALL_OBJS) $$($1_RES) $$($1_MANIFEST) \
         $$($1_VARDEPS_FILE)
 		$$(call LogInfo, Linking executable $$($1_BASENAME))
-		$(call LogFailures, $$($1_OBJECT_DIR)/$$($1_SAFE_NAME)_link.log, $$($1_SAFE_NAME)_link, \
+		$(call LogFailures, $$($1_OBJECT_DIR)/$$($1_SAFE_NAME)_link, $$($1_SAFE_NAME)_link, \
 		    $$($1_LD) $$($1_LDFLAGS) $$($1_EXTRA_LDFLAGS) $$($1_SYSROOT_LDFLAGS) \
 		        $(EXE_OUT_OPTION)$$($1_TARGET) \
 		        $$($1_ALL_OBJS) $$($1_RES) \
diff --git a/make/common/SetupJavaCompilers.gmk b/make/common/SetupJavaCompilers.gmk
index e9e8fc0d538..a381d1c65a4 100644
--- a/make/common/SetupJavaCompilers.gmk
+++ b/make/common/SetupJavaCompilers.gmk
@@ -55,7 +55,7 @@ $(eval $(call SetupJavaCompiler,BOOT_JAVAC, \
 $(eval $(call SetupJavaCompiler,GENERATE_OLDBYTECODE, \
     JVM := $(JAVA_SMALL), \
     JAVAC := $(NEW_JAVAC), \
-    FLAGS := $(BOOT_JDK_SOURCETARGET) -XDignore.symbol.file=true \
+    FLAGS := $(BOOT_JDK_SOURCETARGET) -XDignore.symbol.file=true -XDstringConcat=inline \
         $(DISABLE_WARNINGS) -Xlint:-options, \
     SERVER_DIR := $(SJAVAC_SERVER_DIR), \
     SERVER_JVM := $(SJAVAC_SERVER_JAVA)))
diff --git a/modules.xml b/modules.xml
index c6370d55e1a..3edabfcb6dc 100644
--- a/modules.xml
+++ b/modules.xml
@@ -2,7 +2,7 @@
 
 <!--
 
-   Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+   Copyright (c) 2014, 2016, 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
@@ -1643,6 +1643,10 @@
       <to>jdk.javadoc</to>
       <to>jdk.jdeps</to>
     </export>
+    <export>
+      <name>com.sun.tools.javac.model</name>
+      <to>jdk.javadoc</to>
+    </export>
   </module>
   <module>
     <name>jdk.crypto.ec</name>
@@ -1743,6 +1747,10 @@
       <to>jdk.scripting.nashorn.shell</to>
     </export>
   </module>
+  <module>
+    <name>jdk.internal.opt</name>
+    <depend>java.base</depend>
+  </module>
   <module>
     <name>jdk.jartool</name>
     <depend>java.base</depend>
@@ -1768,6 +1776,12 @@
     <export>
       <name>com.sun.tools.javadoc</name>
     </export>
+    <export>
+      <name>jdk.javadoc.doclet</name>  
+    </export>
+    <export>
+      <name>jdk.javadoc.doclet.taglet</name>  
+    </export>
   </module>
   <module>
     <name>jdk.jcmd</name>
diff --git a/nashorn/.hgtags b/nashorn/.hgtags
index 3884ed80821..9fa99b07ef3 100644
--- a/nashorn/.hgtags
+++ b/nashorn/.hgtags
@@ -336,3 +336,4 @@ d52c09d5d98a81ee6102a25f662ec4b9ae614163 jdk-9+96
 da397aea8adad7e6f743b60bfe0c415fc8508df5 jdk-9+100
 1916a2c680d8c33b59943dbb6dc2dd2000ec821a jdk-9+101
 e5620875888583d35e9492b62fe27e054df26049 jdk-9+102
+c9406f325a23e9093fa667ad3c594e2efe078f47 jdk-9+103
diff --git a/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/TypeConverterFactory.java b/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/TypeConverterFactory.java
index 91e98d29e71..ac8de271267 100644
--- a/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/TypeConverterFactory.java
+++ b/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/TypeConverterFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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
@@ -209,8 +209,8 @@ final class TypeConverterFactory {
                 c.add((ConversionComparator)factory);
             }
         }
-        this.factories = l.toArray(new GuardingTypeConverterFactory[l.size()]);
-        this.comparators = c.toArray(new ConversionComparator[c.size()]);
+        this.factories = l.toArray(new GuardingTypeConverterFactory[0]);
+        this.comparators = c.toArray(new ConversionComparator[0]);
         this.autoConversionStrategy = autoConversionStrategy;
     }
 
@@ -285,7 +285,7 @@ final class TypeConverterFactory {
             return handle;
         }
         final MethodHandle newHandle =
-                MethodHandles.filterArguments(handle, pos, converters.toArray(new MethodHandle[converters.size()]));
+                MethodHandles.filterArguments(handle, pos, converters.toArray(new MethodHandle[0]));
         converters.clear();
         return newHandle;
     }
diff --git a/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/beans/AccessibleMembersLookup.java b/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/beans/AccessibleMembersLookup.java
index 1c1e382ba52..84f9ffa551b 100644
--- a/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/beans/AccessibleMembersLookup.java
+++ b/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/beans/AccessibleMembersLookup.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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
@@ -133,7 +133,7 @@ class AccessibleMembersLookup {
     }
 
     Class<?>[] getInnerClasses() {
-        return innerClasses.toArray(new Class<?>[innerClasses.size()]);
+        return innerClasses.toArray(new Class<?>[0]);
     }
 
     /**
diff --git a/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/linker/support/CompositeGuardingDynamicLinker.java b/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/linker/support/CompositeGuardingDynamicLinker.java
index 2c73f6a7c3c..e8e78efa56d 100644
--- a/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/linker/support/CompositeGuardingDynamicLinker.java
+++ b/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/linker/support/CompositeGuardingDynamicLinker.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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
@@ -112,7 +112,7 @@ public class CompositeGuardingDynamicLinker implements GuardingDynamicLinker {
         for(final GuardingDynamicLinker linker: linkers) {
             l.add(Objects.requireNonNull(linker));
         }
-        this.linkers = l.toArray(new GuardingDynamicLinker[l.size()]);
+        this.linkers = l.toArray(new GuardingDynamicLinker[0]);
     }
 
     /**
diff --git a/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/linker/support/CompositeTypeBasedGuardingDynamicLinker.java b/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/linker/support/CompositeTypeBasedGuardingDynamicLinker.java
index f9685c746b9..cc62743fe1c 100644
--- a/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/linker/support/CompositeTypeBasedGuardingDynamicLinker.java
+++ b/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/linker/support/CompositeTypeBasedGuardingDynamicLinker.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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
@@ -157,7 +157,7 @@ public class CompositeTypeBasedGuardingDynamicLinker implements TypeBasedGuardin
         for(final TypeBasedGuardingDynamicLinker linker: linkers) {
             l.add(Objects.requireNonNull(linker));
         }
-        this.classToLinker = new ClassToLinker(l.toArray(new TypeBasedGuardingDynamicLinker[l.size()]));
+        this.classToLinker = new ClassToLinker(l.toArray(new TypeBasedGuardingDynamicLinker[0]));
     }
 
     /**
diff --git a/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/support/ChainedCallSite.java b/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/support/ChainedCallSite.java
index 28615c52439..0ba7c8b05d7 100644
--- a/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/support/ChainedCallSite.java
+++ b/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/support/ChainedCallSite.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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
@@ -209,7 +209,7 @@ public class ChainedCallSite extends AbstractRelinkableCallSite {
                 invocations = newInvocations.getFirst();
                 break;
             default:
-                invocations = newInvocations.toArray(new GuardedInvocation[newInvocations.size()]);
+                invocations = newInvocations.toArray(new GuardedInvocation[0]);
         }
         setTarget(target);
         return target;
diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/scripting/NashornException.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/scripting/NashornException.java
index b6fa2c6d2c5..1f7631d332d 100644
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/scripting/NashornException.java
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/scripting/NashornException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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
@@ -182,7 +182,7 @@ public abstract class NashornException extends RuntimeException {
                         st.getFileName(), st.getLineNumber()));
             }
         }
-        return filtered.toArray(new StackTraceElement[filtered.size()]);
+        return filtered.toArray(new StackTraceElement[0]);
     }
 
     /**
diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java
index f3cb6b1bae4..a4197188912 100644
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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
@@ -2996,8 +2996,8 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
 
             // Copy values and labels to arrays.
             final int       size   = tree.size();
-            final Integer[] values = tree.keySet().toArray(new Integer[size]);
-            final Label[]   labels = tree.values().toArray(new Label[size]);
+            final Integer[] values = tree.keySet().toArray(new Integer[0]);
+            final Label[]   labels = tree.values().toArray(new Label[0]);
 
             // Discern low, high and range.
             final int lo    = values[0];
@@ -5203,7 +5203,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
                 }
             }
         }
-        return names.toArray(new String[names.size()]);
+        return names.toArray(new String[0]);
     }
 
     private static String commonPrefix(final String s1, final String s2) {
diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/FunctionSignature.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/FunctionSignature.java
index 5e06794609c..ab80c3d3ad2 100644
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/FunctionSignature.java
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/FunctionSignature.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -141,7 +141,7 @@ public final class FunctionSignature {
             paramTypeList.add(paramType.getTypeClass());
         }
 
-        this.methodType = MH.type(returnType.getTypeClass(), paramTypeList.toArray(new Class<?>[paramTypes.length]));
+        this.methodType = MH.type(returnType.getTypeClass(), paramTypeList.toArray(new Class<?>[0]));
     }
 
     /**
diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Block.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Block.java
index 6230de89c85..7683ba5e88b 100644
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Block.java
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Block.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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
@@ -136,7 +136,7 @@ public class Block extends Node implements BreakableNode, Terminal, Flags<Block>
      * @param statements All statements in the block
      */
     public Block(final long token, final int finish, final int flags, final List<Statement> statements) {
-        this(token, finish, flags, statements.toArray(new Statement[statements.size()]));
+        this(token, finish, flags, statements.toArray(new Statement[0]));
     }
 
     private Block(final Block block, final int finish, final List<Statement> statements, final int flags, final Map<String, Symbol> symbols, final LocalVariableConversion conversion) {
diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LiteralNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LiteralNode.java
index 922e51edf26..6fcea43ca4e 100644
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LiteralNode.java
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LiteralNode.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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
@@ -250,7 +250,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
     }
 
     private static Expression[] valueToArray(final List<Expression> value) {
-        return value.toArray(new Expression[value.size()]);
+        return value.toArray(new Expression[0]);
     }
 
     /**
@@ -941,7 +941,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
         }
 
         private ArrayLiteralNode setValue(final LexicalContext lc, final List<Expression> value) {
-            return setValue(lc, value.toArray(new Expression[value.size()]));
+            return setValue(lc, value.toArray(new Expression[0]));
         }
 
         @Override
diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/debug/ASTWriter.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/debug/ASTWriter.java
index f6f0e555ecd..93cede30f6e 100644
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/debug/ASTWriter.java
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/debug/ASTWriter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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
@@ -98,7 +98,7 @@ public final class ASTWriter {
     public Node[] toArray() {
         final List<Node> preorder = new ArrayList<>();
         printAST(new StringBuilder(), preorder, null, "root", root, 0);
-        return preorder.toArray(new Node[preorder.size()]);
+        return preorder.toArray(new Node[0]);
     }
 
     @SuppressWarnings("unchecked")
diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/debug/ObjectSizeCalculator.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/debug/ObjectSizeCalculator.java
index a063ac69b1a..a6d49a17ee5 100644
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/debug/ObjectSizeCalculator.java
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/debug/ObjectSizeCalculator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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
@@ -328,7 +328,7 @@ public final class ObjectSizeCalculator {
             this.fieldsSize = newFieldsSize;
             this.objectSize = roundTo(objectHeaderSize + newFieldsSize, objectPadding);
             this.referenceFields = newReferenceFields.toArray(
-                    new Field[newReferenceFields.size()]);
+                    new Field[0]);
         }
 
         void visit(final Object obj, final ObjectSizeCalculator calc) {
diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeArray.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeArray.java
index 6773840a0a8..06b2b0f72e2 100644
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeArray.java
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeArray.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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
@@ -1264,7 +1264,7 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin
             // behaviour of sort is implementation-defined.
         }
 
-        return list.toArray(new Object[array.length]);
+        return list.toArray(new Object[0]);
     }
 
     /**
diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeDebug.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeDebug.java
index f6039236902..d894abb0cf5 100644
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeDebug.java
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeDebug.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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
@@ -379,7 +379,7 @@ public final class NativeDebug extends ScriptObject {
     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
     public static Object getRuntimeEvents(final Object self) {
         final LinkedList<RuntimeEvent<?>> q = getEventQueue(self);
-        return q.toArray(new RuntimeEvent<?>[q.size()]);
+        return q.toArray(new RuntimeEvent<?>[0]);
     }
 
     /**
diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeFunction.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeFunction.java
index 61abad4a949..3b797947ffa 100644
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeFunction.java
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeFunction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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
@@ -130,7 +130,7 @@ public final class NativeFunction {
             return (Object[])array;
         } else if (array instanceof List) {
             final List<?> list = (List<?>)array;
-            return list.toArray(new Object[list.size()]);
+            return list.toArray(new Object[0]);
         } else if (array == null || array == UNDEFINED) {
             return ScriptRuntime.EMPTY_ARRAY;
         } else if (array instanceof JSObject) {
diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeObject.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeObject.java
index bdd8c2a9cd1..de0cabfb36a 100644
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeObject.java
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeObject.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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
@@ -702,7 +702,7 @@ public final class NativeObject {
             }
 
             if (!propList.isEmpty()) {
-                targetObj.addBoundProperties(sourceObj, propList.toArray(new Property[propList.size()]));
+                targetObj.addBoundProperties(sourceObj, propList.toArray(new Property[0]));
             }
         } else if (source instanceof ScriptObjectMirror) {
             // get enumerable, immediate properties of mirror
@@ -819,7 +819,7 @@ public final class NativeObject {
             }
         }
 
-        targetObj.addBoundProperties(source, properties.toArray(new AccessorProperty[properties.size()]));
+        targetObj.addBoundProperties(source, properties.toArray(new AccessorProperty[0]));
     }
 
     private static MethodHandle getBoundBeanMethodGetter(final Object source, final MethodHandle methodGetter) {
diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyMap.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyMap.java
index d517fb6238e..76711806b76 100644
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyMap.java
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyMap.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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
@@ -377,7 +377,7 @@ public class PropertyMap implements Iterable<Object>, Serializable {
                 if (Context.DEBUG) {
                     protoInvalidations.add(size);
                 }
-                SwitchPoint.invalidateAll(protoSwitches.values().toArray(new SwitchPoint[size]));
+                SwitchPoint.invalidateAll(protoSwitches.values().toArray(new SwitchPoint[0]));
                 protoSwitches.clear();
             }
         }
diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptFunction.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptFunction.java
index a937d383beb..13a61c81c1c 100644
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptFunction.java
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptFunction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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
@@ -553,7 +553,7 @@ public class ScriptFunction extends ScriptObject {
      * @param prototype actual prototype object
      * @return property map
      */
-    private synchronized PropertyMap getAllocatorMap(final ScriptObject prototype) {
+    private PropertyMap getAllocatorMap(final ScriptObject prototype) {
         if (allocatorMap == null || allocatorMap.isInvalidSharedMapFor(prototype)) {
             // The prototype map has changed since this function was last used as constructor.
             // Get a new allocator map.
@@ -993,7 +993,7 @@ public class ScriptFunction extends ScriptObject {
         if (bestInvoker.getSwitchPoints() != null) {
             sps.addAll(Arrays.asList(bestInvoker.getSwitchPoints()));
         }
-        final SwitchPoint[] spsArray = sps.isEmpty() ? null : sps.toArray(new SwitchPoint[sps.size()]);
+        final SwitchPoint[] spsArray = sps.isEmpty() ? null : sps.toArray(new SwitchPoint[0]);
 
         return new GuardedInvocation(
                 boundHandle,
diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java
index 5a47b57aa26..709f2fec9ff 100644
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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
@@ -1334,7 +1334,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
         for (ScriptObject self = this; self != null; self = self.getProto()) {
             keys.addAll(Arrays.asList(self.getOwnKeys(String.class, true, nonEnumerable)));
         }
-        return keys.toArray(new String[keys.size()]);
+        return keys.toArray(new String[0]);
     }
 
     /**
@@ -2146,7 +2146,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
         }
 
         switchPoints.add(getMap().getSwitchPoint(name));
-        return switchPoints.toArray(new SwitchPoint[switchPoints.size()]);
+        return switchPoints.toArray(new SwitchPoint[0]);
     }
 
     private void checkSharedProtoMap() {
@@ -2505,7 +2505,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
             for (ScriptObject self = object; self != null; self = self.getProto()) {
                 keys.addAll(Arrays.asList(self.getOwnKeys(String.class, false, nonEnumerable)));
             }
-            this.values = keys.toArray(new String[keys.size()]);
+            this.values = keys.toArray(new String[0]);
         }
     }
 
@@ -2523,7 +2523,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
                     valueList.add(self.get(key));
                 }
             }
-            this.values = valueList.toArray(new Object[valueList.size()]);
+            this.values = valueList.toArray(new Object[0]);
         }
     }
 
diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Timing.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Timing.java
index 7d5d63661c9..1950bd9048c 100644
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Timing.java
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Timing.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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
@@ -176,7 +176,7 @@ public final class Timing implements Loggable {
             } catch (final IOException e) {
                 throw new RuntimeException(e);
             }
-            return strs.toArray(new String[strs.size()]);
+            return strs.toArray(new String[0]);
         }
 
         @Override
diff --git a/nashorn/test/src/jdk/nashorn/internal/test/framework/ParallelTestRunner.java b/nashorn/test/src/jdk/nashorn/internal/test/framework/ParallelTestRunner.java
index 2c206d7caba..f1ae70a6863 100644
--- a/nashorn/test/src/jdk/nashorn/internal/test/framework/ParallelTestRunner.java
+++ b/nashorn/test/src/jdk/nashorn/internal/test/framework/ParallelTestRunner.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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
@@ -166,7 +166,7 @@ public class ParallelTestRunner {
             final List<String> args = getCompilerArgs();
             int errors;
             try {
-                errors = evaluateScript(out, err, args.toArray(new String[args.size()]));
+                errors = evaluateScript(out, err, args.toArray(new String[0]));
             } catch (final AssertionError e) {
                 final PrintWriter writer = new PrintWriter(err);
                 e.printStackTrace(writer);
@@ -199,7 +199,7 @@ public class ParallelTestRunner {
             final ByteArrayOutputStream err = new ByteArrayOutputStream();
 
             try {
-                final int errors = evaluateScript(out, err, args.toArray(new String[args.size()]));
+                final int errors = evaluateScript(out, err, args.toArray(new String[0]));
 
                 if (errors != 0 || err.size() > 0) {
                     if (expectRunFailure) {
diff --git a/nashorn/test/src/jdk/nashorn/internal/test/framework/ScriptRunnable.java b/nashorn/test/src/jdk/nashorn/internal/test/framework/ScriptRunnable.java
index 87a1abf6fe7..8ca3405701b 100644
--- a/nashorn/test/src/jdk/nashorn/internal/test/framework/ScriptRunnable.java
+++ b/nashorn/test/src/jdk/nashorn/internal/test/framework/ScriptRunnable.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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,7 +108,7 @@ public final class ScriptRunnable extends AbstractScriptRunnable implements ITes
         int errors;
 
         try {
-            errors = evaluateScript(out, err, args.toArray(new String[args.size()]));
+            errors = evaluateScript(out, err, args.toArray(new String[0]));
         } catch (final AssertionError e) {
             final PrintWriter writer = new PrintWriter(err);
             e.printStackTrace(writer);
@@ -144,7 +144,7 @@ public final class ScriptRunnable extends AbstractScriptRunnable implements ITes
         final File errorFileHandle  = new File(errorFileName);
 
         try (OutputStream outputFile = new FileOutputStream(outputFileName); OutputStream errorFile = new FileOutputStream(errorFileName)) {
-            final int errors = evaluateScript(outputFile, errorFile, args.toArray(new String[args.size()]));
+            final int errors = evaluateScript(outputFile, errorFile, args.toArray(new String[0]));
 
             if (errors != 0 || errorFileHandle.length() > 0) {
                 if (expectRunFailure) {
diff --git a/nashorn/test/src/jdk/nashorn/internal/test/framework/TestFinder.java b/nashorn/test/src/jdk/nashorn/internal/test/framework/TestFinder.java
index fa5ef573ae7..22f03ccad77 100644
--- a/nashorn/test/src/jdk/nashorn/internal/test/framework/TestFinder.java
+++ b/nashorn/test/src/jdk/nashorn/internal/test/framework/TestFinder.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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
@@ -392,7 +392,7 @@ public final class TestFinder {
         if (hasOptimisticOverride()) {
             final List<String> newList = new ArrayList<>(Arrays.asList(args));
             newList.add("--optimistic-types=" + OPTIMISTIC_OVERRIDE);
-            return newList.toArray(new String[newList.size()]);
+            return newList.toArray(new String[0]);
         }
         return args;
     }
diff --git a/nashorn/tools/fxshell/jdk/nashorn/tools/FXShell.java b/nashorn/tools/fxshell/jdk/nashorn/tools/FXShell.java
index fe16ca204d5..b6be4940b9b 100644
--- a/nashorn/tools/fxshell/jdk/nashorn/tools/FXShell.java
+++ b/nashorn/tools/fxshell/jdk/nashorn/tools/FXShell.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2016, 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
@@ -134,7 +134,7 @@ public class FXShell extends Application {
         }
 
         // Create a Nashorn script engine with specified arguments.
-        engine = factory.getScriptEngine(args.toArray(new String[args.size()]));
+        engine = factory.getScriptEngine(args.toArray(new String[0]));
 
         // Load initial scripts.
         for (String path : paths) {