# HG changeset patch # User lana # Date 1492100838 0 # Node ID 2c3a10f7a923fda5605aed08c169e077a45f1565 # Parent ddaea4f20a7b8103d79957be9045875afbde9d1a# Parent 9ec66fa5cc1308ca49f9829ece09b0cd59e68f68 Merge diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/src/java.base/share/classes/com/sun/crypto/provider/CounterMode.java --- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/CounterMode.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/CounterMode.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,10 +42,10 @@ * @author Andreas Sterbenz * @since 1.4.2 */ -final class CounterMode extends FeedbackCipher { +class CounterMode extends FeedbackCipher { // current counter value - private final byte[] counter; + final byte[] counter; // encrypted bytes of the previous counter value private final byte[] encryptedCounter; diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/src/java.base/share/classes/com/sun/crypto/provider/GCTR.java --- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/GCTR.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/GCTR.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2017 Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,52 +29,43 @@ package com.sun.crypto.provider; -import java.security.*; -import javax.crypto.*; +import javax.crypto.IllegalBlockSizeException; import static com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE; /** * This class represents the GCTR function defined in NIST 800-38D - * under section 6.5. It needs to be constructed w/ an initialized - * cipher object, and initial counter block(ICB). Given an input X - * of arbitrary length, it processes and returns an output which has - * the same length as X. The invariants of this class are: - * - * (1) The length of intialCounterBlk (and also of its clones, e.g., - * fields counter and counterSave) is equal to AES_BLOCK_SIZE. - * - * (2) After construction, the field counter never becomes null, it - * always contains a byte array of length AES_BLOCK_SIZE. + * under section 6.5. With a given cipher object and initial counter + * block, a counter mode operation is performed. Blocksize is limited + * to 16 bytes. * * If any invariant is broken, failures can occur because the * AESCrypt.encryptBlock method can be intrinsified on the HotSpot VM * (see JDK-8067648 for details). * + * The counter mode operations can be intrinsified and parallelized + * by using CounterMode.implCrypt() if HotSpot VM supports it on the + * architecture. + * *

This function is used in the implementation of GCM mode. * * @since 1.8 */ -final class GCTR { - - // these fields should not change after the object has been constructed - private final SymmetricCipher aes; - private final byte[] icb; +final class GCTR extends CounterMode { - // the current counter value - private byte[] counter; - - // needed for save/restore calls - private byte[] counterSave = null; - - // NOTE: cipher should already be initialized GCTR(SymmetricCipher cipher, byte[] initialCounterBlk) { - this.aes = cipher; + super(cipher); if (initialCounterBlk.length != AES_BLOCK_SIZE) { throw new RuntimeException("length of initial counter block (" + initialCounterBlk.length + ") not equal to AES_BLOCK_SIZE (" + AES_BLOCK_SIZE + ")"); } - this.icb = initialCounterBlk; - this.counter = icb.clone(); + + iv = initialCounterBlk; + reset(); + } + + @Override + String getFeedback() { + return "GCTR"; } // input must be multiples of 128-bit blocks when calling update @@ -89,23 +80,11 @@ throw new RuntimeException("output buffer too small"); } - byte[] encryptedCntr = new byte[AES_BLOCK_SIZE]; - - int numOfCompleteBlocks = inLen / AES_BLOCK_SIZE; - for (int i = 0; i < numOfCompleteBlocks; i++) { - aes.encryptBlock(counter, 0, encryptedCntr, 0); - for (int n = 0; n < AES_BLOCK_SIZE; n++) { - int index = (i * AES_BLOCK_SIZE + n); - out[outOfs + index] = - (byte) ((in[inOfs + index] ^ encryptedCntr[n])); - } - GaloisCounterMode.increment32(counter); - } - return inLen; + return encrypt(in, inOfs, inLen, out, outOfs); } // input can be arbitrary size when calling doFinal - protected int doFinal(byte[] in, int inOfs, int inLen, byte[] out, + int doFinal(byte[] in, int inOfs, int inLen, byte[] out, int outOfs) throws IllegalBlockSizeException { try { if (inLen < 0) { @@ -118,7 +97,7 @@ if (lastBlockSize != 0) { // do the last partial block byte[] encryptedCntr = new byte[AES_BLOCK_SIZE]; - aes.encryptBlock(counter, 0, encryptedCntr, 0); + embeddedCipher.encryptBlock(counter, 0, encryptedCntr, 0); for (int n = 0; n < lastBlockSize; n++) { out[outOfs + completeBlkLen + n] = (byte) ((in[inOfs + completeBlkLen + n] ^ @@ -131,28 +110,4 @@ } return inLen; } - - /** - * Resets the content of this object to when it's first constructed. - */ - void reset() { - System.arraycopy(icb, 0, counter, 0, icb.length); - counterSave = null; - } - - /** - * Save the current content of this object. - */ - void save() { - this.counterSave = this.counter.clone(); - } - - /** - * Restores the content of this object to the previous saved one. - */ - void restore() { - if (this.counterSave != null) { - this.counter = this.counterSave; - } - } } diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/src/java.base/share/classes/java/io/FilePermission.java --- a/jdk/src/java.base/share/classes/java/io/FilePermission.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/src/java.base/share/classes/java/io/FilePermission.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -209,6 +209,10 @@ private static final Path here = builtInFS.getPath( GetPropertyAction.privilegedGetProperty("user.dir")); + private static final Path EMPTY_PATH = builtInFS.getPath(""); + private static final Path DASH_PATH = builtInFS.getPath("-"); + private static final Path DOTDOT_PATH = builtInFS.getPath(".."); + /** * A private constructor that clones some and updates some, * always with a different name. @@ -341,7 +345,7 @@ .normalize(); // lastName should always be non-null now Path lastName = npath.getFileName(); - if (lastName != null && lastName.toString().equals("-")) { + if (lastName != null && lastName.equals(DASH_PATH)) { directory = true; recursive = !rememberStar; npath = npath.getParent(); @@ -679,23 +683,76 @@ * @return the depth in between */ private static int containsPath(Path p1, Path p2) { - Path p; - try { - p = p2.relativize(p1).normalize(); - if (p.getName(0).toString().isEmpty()) { - return 0; - } else { - for (Path item: p) { - String s = item.toString(); - if (!s.equals("..")) { - return -1; - } - } - return p.getNameCount(); - } - } catch (IllegalArgumentException iae) { + + // Two paths must have the same root. For example, + // there is no contains relation between any two of + // "/x", "x", "C:/x", "C:x", and "//host/share/x". + if (!Objects.equals(p1.getRoot(), p2.getRoot())) { return -1; } + + // Empty path (i.e. "." or "") is a strange beast, + // because its getNameCount()==1 but getName(0) is null. + // It's better to deal with it separately. + if (p1.equals(EMPTY_PATH)) { + if (p2.equals(EMPTY_PATH)) { + return 0; + } else if (p2.getName(0).equals(DOTDOT_PATH)) { + // "." contains p2 iif p2 has no "..". Since a + // a normalized path can only have 0 or more + // ".." at the beginning. We only need to look + // at the head. + return -1; + } else { + // and the distance is p2's name count. i.e. + // 3 between "." and "a/b/c". + return p2.getNameCount(); + } + } else if (p2.equals(EMPTY_PATH)) { + int c1 = p1.getNameCount(); + if (!p1.getName(c1 - 1).equals(DOTDOT_PATH)) { + // "." is inside p1 iif p1 is 1 or more "..". + // For the same reason above, we only need to + // look at the tail. + return -1; + } + // and the distance is the count of ".." + return c1; + } + + // Good. No more empty paths. + + // Common heads are removed + + int c1 = p1.getNameCount(); + int c2 = p2.getNameCount(); + + int n = Math.min(c1, c2); + int i = 0; + while (i < n) { + if (!p1.getName(i).equals(p2.getName(i))) + break; + i++; + } + + // for p1 containing p2, p1 must be 0-or-more "..", + // and p2 cannot have "..". For the same reason, we only + // check tail of p1 and head of p2. + if (i < c1 && !p1.getName(c1 - 1).equals(DOTDOT_PATH)) { + return -1; + } + + if (i < c2 && p2.getName(i).equals(DOTDOT_PATH)) { + return -1; + } + + // and the distance is the name counts added (after removing + // the common heads). + + // For example: p1 = "../../..", p2 = "../a". + // After removing the common heads, they become "../.." and "a", + // and the distance is (3-1)+(2-1) = 3. + return c1 - i + c2 - i; } /** diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java --- a/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -307,6 +307,8 @@ * sequence. */ public int codePointAt(int index) { + int count = this.count; + byte[] value = this.value; checkIndex(index, count); if (isLatin1()) { return value[index] & 0xff; @@ -560,11 +562,7 @@ val[count++] = 'l'; val[count++] = 'l'; } else { - checkOffset(count + 4, val.length >> 1); - StringUTF16.putChar(val, count++, 'n'); - StringUTF16.putChar(val, count++, 'u'); - StringUTF16.putChar(val, count++, 'l'); - StringUTF16.putChar(val, count++, 'l'); + count = StringUTF16.putCharsAt(val, count, 'n', 'u', 'l', 'l'); } this.count = count; return this; @@ -695,18 +693,9 @@ } } else { if (b) { - checkOffset(count + 4, val.length >> 1); - StringUTF16.putChar(val, count++, 't'); - StringUTF16.putChar(val, count++, 'r'); - StringUTF16.putChar(val, count++, 'u'); - StringUTF16.putChar(val, count++, 'e'); + count = StringUTF16.putCharsAt(val, count, 't', 'r', 'u', 'e'); } else { - checkOffset(count + 5, val.length >> 1); - StringUTF16.putChar(val, count++, 'f'); - StringUTF16.putChar(val, count++, 'a'); - StringUTF16.putChar(val, count++, 'l'); - StringUTF16.putChar(val, count++, 's'); - StringUTF16.putChar(val, count++, 'e'); + count = StringUTF16.putCharsAt(val, count, 'f', 'a', 'l', 's', 'e'); } } this.count = count; @@ -755,16 +744,15 @@ * @return a reference to this object. */ public AbstractStringBuilder append(int i) { + int count = this.count; int spaceNeeded = count + Integer.stringSize(i); ensureCapacityInternal(spaceNeeded); if (isLatin1()) { Integer.getChars(i, spaceNeeded, value); } else { - byte[] val = this.value; - checkOffset(spaceNeeded, val.length >> 1); - Integer.getCharsUTF16(i, spaceNeeded, val); + StringUTF16.getChars(i, count, spaceNeeded, value); } - count = spaceNeeded; + this.count = spaceNeeded; return this; } @@ -781,16 +769,15 @@ * @return a reference to this object. */ public AbstractStringBuilder append(long l) { + int count = this.count; int spaceNeeded = count + Long.stringSize(l); ensureCapacityInternal(spaceNeeded); if (isLatin1()) { Long.getChars(l, spaceNeeded, value); } else { - byte[] val = this.value; - checkOffset(spaceNeeded, val.length >> 1); - Long.getCharsUTF16(l, spaceNeeded, val); + StringUTF16.getChars(l, count, spaceNeeded, value); } - count = spaceNeeded; + this.count = spaceNeeded; return this; } @@ -843,6 +830,7 @@ * greater than {@code end}. */ public AbstractStringBuilder delete(int start, int end) { + int count = this.count; if (end > count) { end = count; } @@ -850,7 +838,7 @@ int len = end - start; if (len > 0) { shift(end, -len); - count -= len; + this.count = count - len; } return this; } @@ -925,6 +913,7 @@ * greater than {@code end}. */ public AbstractStringBuilder replace(int start, int end, String str) { + int count = this.count; if (end > count) { end = count; } @@ -933,7 +922,7 @@ int newCount = count + len - (end - start); ensureCapacityInternal(newCount); shift(end, newCount - count); - count = newCount; + this.count = newCount; putStringAt(start, str); return this; } @@ -1500,40 +1489,11 @@ val[k] = cj; } } else { - checkOffset(count, val.length >> 1); - boolean hasSurrogates = false; - for (int j = (n-1) >> 1; j >= 0; j--) { - int k = n - j; - char cj = StringUTF16.getChar(val, j); - char ck = StringUTF16.getChar(val, k); - StringUTF16.putChar(val, j, ck); - StringUTF16.putChar(val, k, cj); - if (Character.isSurrogate(cj) || - Character.isSurrogate(ck)) { - hasSurrogates = true; - } - } - if (hasSurrogates) { - reverseAllValidSurrogatePairs(val, count); - } + StringUTF16.reverse(val, count); } return this; } - /** Outlined helper method for reverse() */ - private void reverseAllValidSurrogatePairs(byte[] val, int count) { - for (int i = 0; i < count - 1; i++) { - char c2 = StringUTF16.getChar(val, i); - if (Character.isLowSurrogate(c2)) { - char c1 = StringUTF16.getChar(val, i + 1); - if (Character.isHighSurrogate(c1)) { - StringUTF16.putChar(val, i++, c1); - StringUTF16.putChar(val, i, c2); - } - } - } - } - /** * Returns a string representing the data in this sequence. * A new {@code String} object is allocated and initialized to @@ -1682,6 +1642,7 @@ } private final void appendChars(char[] s, int off, int end) { + int count = this.count; if (isLatin1()) { byte[] val = this.value; for (int i = off, j = count; i < end; i++) { @@ -1689,17 +1650,17 @@ if (StringLatin1.canEncode(c)) { val[j++] = (byte)c; } else { - count = j; + this.count = count = j; inflate(); StringUTF16.putCharsSB(this.value, j, s, i, end); - count += end - i; + this.count = count + end - i; return; } } } else { StringUTF16.putCharsSB(this.value, count, s, off, end); } - count += end - off; + this.count = count + end - off; } private final void appendChars(CharSequence s, int off, int end) { diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/src/java.base/share/classes/java/lang/Integer.java --- a/jdk/src/java.base/share/classes/java/lang/Integer.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/src/java.base/share/classes/java/lang/Integer.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -386,7 +386,7 @@ } /** byte[]/UTF16 version */ - static void formatUnsignedIntUTF16(int val, int shift, byte[] buf, int offset, int len) { + private static void formatUnsignedIntUTF16(int val, int shift, byte[] buf, int offset, int len) { int charPos = offset + len; int radix = 1 << shift; int mask = radix - 1; @@ -442,7 +442,7 @@ return new String(buf, LATIN1); } else { byte[] buf = new byte[size * 2]; - getCharsUTF16(i, size, buf); + StringUTF16.getChars(i, size, buf); return new String(buf, UTF16); } } @@ -516,49 +516,6 @@ return charPos; } - /** - * 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; - - boolean negative = (i < 0); - if (!negative) { - i = -i; - } - - // Get 2 digits/iteration using ints - while (i <= -100) { - q = i / 100; - r = (q * 100) - i; - i = q; - StringUTF16.putChar(buf, --charPos, DigitOnes[r]); - StringUTF16.putChar(buf, --charPos, DigitTens[r]); - } - - // We know there are at most two digits left at this point. - q = i / 10; - r = (q * 10) - i; - StringUTF16.putChar(buf, --charPos, '0' + r); - - // Whatever left is the remaining digit. - if (q < 0) { - StringUTF16.putChar(buf, --charPos, '0' - q); - } - - if (negative) { - StringUTF16.putChar(buf, --charPos, '-'); - } - return charPos; - } - // Left here for compatibility reasons, see JDK-8143900. static final int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999, Integer.MAX_VALUE }; diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/src/java.base/share/classes/java/lang/Long.java --- a/jdk/src/java.base/share/classes/java/lang/Long.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/src/java.base/share/classes/java/lang/Long.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -414,7 +414,7 @@ } /** byte[]/UTF16 version */ - static void formatUnsignedLong0UTF16(long val, int shift, byte[] buf, int offset, int len) { + private static void formatUnsignedLong0UTF16(long val, int shift, byte[] buf, int offset, int len) { int charPos = offset + len; int radix = 1 << shift; int mask = radix - 1; @@ -475,7 +475,7 @@ return new String(buf, LATIN1); } else { byte[] buf = new byte[size * 2]; - getCharsUTF16(i, size, buf); + StringUTF16.getChars(i, size, buf); return new String(buf, UTF16); } } @@ -562,61 +562,6 @@ } /** - * 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; - - boolean negative = (i < 0); - if (!negative) { - i = -i; - } - - // Get 2 digits/iteration using longs until quotient fits into an int - while (i <= Integer.MIN_VALUE) { - q = i / 100; - r = (int)((q * 100) - i); - i = q; - StringUTF16.putChar(buf, --charPos, Integer.DigitOnes[r]); - StringUTF16.putChar(buf, --charPos, Integer.DigitTens[r]); - } - - // Get 2 digits/iteration using ints - int q2; - int i2 = (int)i; - while (i2 <= -100) { - q2 = i2 / 100; - r = (q2 * 100) - i2; - i2 = q2; - StringUTF16.putChar(buf, --charPos, Integer.DigitOnes[r]); - StringUTF16.putChar(buf, --charPos, Integer.DigitTens[r]); - } - - // We know there are at most two digits left at this point. - q2 = i2 / 10; - r = (q2 * 10) - i2; - StringUTF16.putChar(buf, --charPos, '0' + r); - - // Whatever left is the remaining digit. - if (q2 < 0) { - StringUTF16.putChar(buf, --charPos, '0' - q2); - } - - if (negative) { - StringUTF16.putChar(buf, --charPos, '-'); - } - return charPos; - } - - /** * Returns the string representation size for a given long value. * * @param x long value diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/src/java.base/share/classes/java/lang/Process.java --- a/jdk/src/java.base/share/classes/java/lang/Process.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/src/java.base/share/classes/java/lang/Process.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -80,10 +80,10 @@ * *

Subclasses of Process should override the {@link #onExit()} and * {@link #toHandle()} methods to provide a fully functional Process including the - * {@link #getPid() process id}, - * {@link #info() information about the process}, - * {@link #children() direct children}, and - * {@link #descendants() direct children plus descendants of those children} of the process. + * {@linkplain #pid() process id}, + * {@linkplain #info() information about the process}, + * {@linkplain #children() direct children}, and + * {@linkplain #descendants() direct children plus descendants of those children} of the process. * Delegating to the underlying Process or ProcessHandle is typically * easiest and most efficient. * @@ -237,14 +237,14 @@ /** * Kills the process. * Whether the process represented by this {@code Process} object is - * {@link #supportsNormalTermination normally terminated} or not is + * {@linkplain #supportsNormalTermination normally terminated} or not is * implementation dependent. * Forcible process destruction is defined as the immediate termination of a * process, whereas normal termination allows the process to shut down cleanly. * If the process is not alive, no action is taken. *

* The {@link java.util.concurrent.CompletableFuture} from {@link #onExit} is - * {@link java.util.concurrent.CompletableFuture#complete completed} + * {@linkplain java.util.concurrent.CompletableFuture#complete completed} * when the process has terminated. */ public abstract void destroy(); @@ -257,7 +257,7 @@ * If the process is not alive, no action is taken. *

* The {@link java.util.concurrent.CompletableFuture} from {@link #onExit} is - * {@link java.util.concurrent.CompletableFuture#complete completed} + * {@linkplain java.util.concurrent.CompletableFuture#complete completed} * when the process has terminated. *

* Invoking this method on {@code Process} objects returned by @@ -335,15 +335,15 @@ * * @implSpec * The implementation of this method returns the process id as: - * {@link #toHandle toHandle().getPid()}. + * {@link #toHandle toHandle().pid()}. * * @return the native process id of the process * @throws UnsupportedOperationException if the Process implementation * does not support this operation * @since 9 */ - public long getPid() { - return toHandle().getPid(); + public long pid() { + return toHandle().pid(); } /** @@ -357,9 +357,9 @@ *

* Calling {@code onExit().get()} waits for the process to terminate and returns * the Process. The future can be used to check if the process is - * {@link java.util.concurrent.CompletableFuture#isDone done} or to - * {@link java.util.concurrent.CompletableFuture#get() wait} for it to terminate. - * {@link java.util.concurrent.CompletableFuture#cancel(boolean) Cancelling} + * {@linkplain java.util.concurrent.CompletableFuture#isDone done} or to + * {@linkplain java.util.concurrent.CompletableFuture#get() wait} for it to terminate. + * {@linkplain java.util.concurrent.CompletableFuture#cancel(boolean) Cancelling} * the CompletableFuture does not affect the Process. *

* Processes returned from {@link ProcessBuilder#start} override the @@ -389,7 +389,7 @@ * {@code waitFor} is interrupted, the thread's interrupt status is preserved. *

* When {@link #waitFor()} returns successfully the CompletableFuture is - * {@link java.util.concurrent.CompletableFuture#complete completed} regardless + * {@linkplain java.util.concurrent.CompletableFuture#complete completed} regardless * of the exit status of the process. * * This implementation may consume a lot of memory for thread stacks if a @@ -463,7 +463,7 @@ * This implementation throws an instance of * {@link java.lang.UnsupportedOperationException} and performs no other action. * Subclasses should override this method to provide a ProcessHandle for the - * process. The methods {@link #getPid}, {@link #info}, {@link #children}, + * process. The methods {@link #pid}, {@link #info}, {@link #children}, * and {@link #descendants}, unless overridden, operate on the ProcessHandle. * * @return Returns a ProcessHandle for the Process @@ -500,10 +500,10 @@ /** * Returns a snapshot of the direct children of the process. * The parent of a direct child process is the process. - * Typically, a process that is {@link #isAlive not alive} has no children. + * Typically, a process that is {@linkplain #isAlive not alive} has no children. *

* Note that processes are created and terminate asynchronously. - * There is no guarantee that a process is {@link #isAlive alive}. + * There is no guarantee that a process is {@linkplain #isAlive alive}. * * * @implSpec @@ -526,10 +526,10 @@ * Returns a snapshot of the descendants of the process. * The descendants of a process are the children of the process * plus the descendants of those children, recursively. - * Typically, a process that is {@link #isAlive not alive} has no children. + * Typically, a process that is {@linkplain #isAlive not alive} has no children. *

* Note that processes are created and terminate asynchronously. - * There is no guarantee that a process is {@link #isAlive alive}. + * There is no guarantee that a process is {@linkplain #isAlive alive}. * * * @implSpec diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/src/java.base/share/classes/java/lang/ProcessHandle.java --- a/jdk/src/java.base/share/classes/java/lang/ProcessHandle.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/src/java.base/share/classes/java/lang/ProcessHandle.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -104,7 +104,7 @@ * @throws UnsupportedOperationException if the implementation * does not support this operation */ - long getPid(); + long pid(); /** * Returns an {@code Optional} for an existing native process. @@ -383,7 +383,7 @@ /** * Returns a hash code value for this ProcessHandle. * The hashcode value follows the general contract for {@link Object#hashCode()}. - * The value is a function of the {@link #getPid getPid()} value and + * The value is a function of the {@link #pid pid()} value and * may be a function of additional information to uniquely identify the process. * If two ProcessHandles are equal according to the {@link #equals(Object) equals} * method, then calling the hashCode method on each of the two objects diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/src/java.base/share/classes/java/lang/ProcessHandleImpl.java --- a/jdk/src/java.base/share/classes/java/lang/ProcessHandleImpl.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/src/java.base/share/classes/java/lang/ProcessHandleImpl.java Thu Apr 13 16:27:18 2017 +0000 @@ -35,11 +35,7 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Executor; import java.util.concurrent.Executors; -import java.util.concurrent.ForkJoinPool; -import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadFactory; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -176,7 +172,7 @@ throw new IllegalStateException("onExit for current process not allowed"); } - return ProcessHandleImpl.completion(getPid(), false) + return ProcessHandleImpl.completion(pid(), false) .handleAsync((exitStatus, unusedThrowable) -> this); } @@ -259,7 +255,7 @@ * @return the native process ID */ @Override - public long getPid() { + public long pid() { return pid; } diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/src/java.base/share/classes/java/lang/String.java --- a/jdk/src/java.base/share/classes/java/lang/String.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/src/java.base/share/classes/java/lang/String.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1064,11 +1064,7 @@ if (!isLatin1()) { // utf16 str and latin1 abs can never be "equal" return false; } - for (int i = 0; i < len; i++) { - if ((char)(v1[i] & 0xff) != StringUTF16.getChar(v2, i)) { - return false; - } - } + return StringUTF16.contentEquals(v1, v2, len); } return true; } @@ -1120,10 +1116,8 @@ } } } else { - for (int i = 0; i < n; i++) { - if (StringUTF16.getChar(val, i) != cs.charAt(i)) { - return false; - } + if (!StringUTF16.contentEquals(val, cs, n)) { + return false; } } return true; @@ -1734,6 +1728,9 @@ if (tgtCount == 0) { return fromIndex; } + if (tgtCount > srcCount) { + return -1; + } if (srcCoder == tgtCoder) { return srcCoder == LATIN1 ? StringLatin1.indexOf(src, srcCount, tgt, tgtCount, fromIndex) @@ -1792,7 +1789,7 @@ * is the string being searched for. * * @param src the characters being searched. - * @param srcCoder coder handles the mapping between bytes/chars + * @param srcCoder coder handles the mapping between bytes/chars * @param srcCount count of the source string. * @param tgt the characters being searched for. * @param fromIndex the index to begin searching from. @@ -1807,12 +1804,12 @@ * consistency, don't check for null str. */ int rightIndex = srcCount - tgtCount; + if (fromIndex > rightIndex) { + fromIndex = rightIndex; + } if (fromIndex < 0) { return -1; } - if (fromIndex > rightIndex) { - fromIndex = rightIndex; - } /* Empty string always matches. */ if (tgtCount == 0) { return fromIndex; @@ -1825,31 +1822,8 @@ if (srcCoder == LATIN1) { // && tgtCoder == UTF16 return -1; } - // srcCoder == UTF16 && tgtCoder == LATIN1 - int min = tgtCount - 1; - int i = min + fromIndex; - int strLastIndex = tgtCount - 1; - - char strLastChar = (char)(tgt[strLastIndex] & 0xff); - startSearchForLastChar: - while (true) { - while (i >= min && StringUTF16.getChar(src, i) != strLastChar) { - i--; - } - if (i < min) { - return -1; - } - int j = i - 1; - int start = j - strLastIndex; - int k = strLastIndex - 1; - while (j > start) { - if (StringUTF16.getChar(src, j--) != (tgt[k--] & 0xff)) { - i--; - continue startSearchForLastChar; - } - } - return start + 1; - } + // srcCoder == UTF16 && tgtCoder == LATIN1 + return StringUTF16.lastIndexOfLatin1(src, srcCount, tgt, tgtCount, fromIndex); } /** @@ -3078,7 +3052,8 @@ */ static void checkIndex(int index, int length) { if (index < 0 || index >= length) { - throw new StringIndexOutOfBoundsException("index " + index); + throw new StringIndexOutOfBoundsException("index " + index + + ",length " + length); } } @@ -3116,7 +3091,7 @@ * If {@code begin} is negative, {@code begin} is greater than * {@code end}, or {@code end} is greater than {@code length}. */ - private static void checkBoundsBeginEnd(int begin, int end, int length) { + static void checkBoundsBeginEnd(int begin, int end, int length) { if (begin < 0 || begin > end || end > length) { throw new StringIndexOutOfBoundsException( "begin " + begin + ", end " + end + ", length " + length); diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/src/java.base/share/classes/java/lang/StringConcatHelper.java --- a/jdk/src/java.base/share/classes/java/lang/StringConcatHelper.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/src/java.base/share/classes/java/lang/StringConcatHelper.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -293,7 +293,7 @@ if (coder == String.LATIN1) { return Integer.getChars(value, index, buf); } else { - return Integer.getCharsUTF16(value, index, buf); + return StringUTF16.getChars(value, index, buf); } } @@ -311,7 +311,7 @@ if (coder == String.LATIN1) { return Long.getChars(value, index, buf); } else { - return Long.getCharsUTF16(value, index, buf); + return StringUTF16.getChars(value, index, buf); } } diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/src/java.base/share/classes/java/lang/StringLatin1.java --- a/jdk/src/java.base/share/classes/java/lang/StringLatin1.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/src/java.base/share/classes/java/lang/StringLatin1.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,7 +36,6 @@ import static java.lang.String.LATIN1; import static java.lang.String.UTF16; import static java.lang.String.checkOffset; -import static java.lang.String.checkBoundsOffCount; final class StringLatin1 { @@ -566,11 +565,7 @@ // inflatedCopy byte[] -> byte[] @HotSpotIntrinsicCandidate public static void inflate(byte[] src, int srcOff, byte[] dst, int dstOff, int len) { - // We need a range check here because 'putChar' has no checks - checkBoundsOffCount(dstOff << 1, len << 1, dst.length); - for (int i = 0; i < len; i++) { - StringUTF16.putChar(dst, dstOff++, src[srcOff++] & 0xff); - } + StringUTF16.inflate(src, srcOff, dst, dstOff, len); } static class CharsSpliterator implements Spliterator.OfInt { diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/src/java.base/share/classes/java/lang/StringUTF16.java --- a/jdk/src/java.base/share/classes/java/lang/StringUTF16.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/src/java.base/share/classes/java/lang/StringUTF16.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,12 +30,11 @@ import java.util.Spliterator; import java.util.function.IntConsumer; import jdk.internal.HotSpotIntrinsicCandidate; +import jdk.internal.vm.annotation.ForceInline; +import jdk.internal.vm.annotation.DontInline; import static java.lang.String.UTF16; import static java.lang.String.LATIN1; -import static java.lang.String.checkIndex; -import static java.lang.String.checkOffset; -import static java.lang.String.checkBoundsOffCount; final class StringUTF16 { @@ -51,33 +50,37 @@ } @HotSpotIntrinsicCandidate - public static void putChar(byte[] val, int index, int c) { + // intrinsic performs no bounds checks + static void putChar(byte[] val, int index, int c) { + assert index >= 0 && index < length(val) : "Trusted caller missed bounds check"; index <<= 1; val[index++] = (byte)(c >> HI_BYTE_SHIFT); val[index] = (byte)(c >> LO_BYTE_SHIFT); } @HotSpotIntrinsicCandidate - public static char getChar(byte[] val, int index) { + // intrinsic performs no bounds checks + static char getChar(byte[] val, int index) { + assert index >= 0 && index < length(val) : "Trusted caller missed bounds check"; index <<= 1; return (char)(((val[index++] & 0xff) << HI_BYTE_SHIFT) | ((val[index] & 0xff) << LO_BYTE_SHIFT)); } - public static char charAt(byte[] value, int index) { - if (index < 0 || index >= value.length >> 1) { - throw new StringIndexOutOfBoundsException(index); - } - return getChar(value, index); - } - public static int length(byte[] value) { return value.length >> 1; } - public static int codePointAt(byte[] value, int index, int end) { + private static int codePointAt(byte[] value, int index, int end, boolean checked) { + assert index < end; + if (checked) { + checkIndex(index, value); + } char c1 = getChar(value, index); if (Character.isHighSurrogate(c1) && ++index < end) { + if (checked) { + checkIndex(index, value); + } char c2 = getChar(value, index); if (Character.isLowSurrogate(c2)) { return Character.toCodePoint(c1, c2); @@ -86,10 +89,22 @@ return c1; } - public static int codePointBefore(byte[] value, int index) { - char c2 = getChar(value, --index); + public static int codePointAt(byte[] value, int index, int end) { + return codePointAt(value, index, end, false /* unchecked */); + } + + private static int codePointBefore(byte[] value, int index, boolean checked) { + --index; + if (checked) { + checkIndex(index, value); + } + char c2 = getChar(value, index); if (Character.isLowSurrogate(c2) && index > 0) { - char c1 = getChar(value, --index); + --index; + if (checked) { + checkIndex(index, value); + } + char c1 = getChar(value, index); if (Character.isHighSurrogate(c1)) { return Character.toCodePoint(c1, c2); } @@ -97,11 +112,19 @@ return c2; } - public static int codePointCount(byte[] value, int beginIndex, int endIndex) { + public static int codePointBefore(byte[] value, int index) { + return codePointBefore(value, index, false /* unchecked */); + } + + private static int codePointCount(byte[] value, int beginIndex, int endIndex, boolean checked) { + assert beginIndex <= endIndex; int count = endIndex - beginIndex; - for (int i = beginIndex; i < endIndex; ) { + int i = beginIndex; + if (checked && i < endIndex) { + checkBoundsBeginEnd(i, endIndex, value); + } + for (; i < endIndex - 1; ) { if (Character.isHighSurrogate(getChar(value, i++)) && - i < endIndex && Character.isLowSurrogate(getChar(value, i))) { count--; i++; @@ -110,6 +133,10 @@ return count; } + public static int codePointCount(byte[] value, int beginIndex, int endIndex) { + return codePointCount(value, beginIndex, endIndex, false /* unchecked */); + } + public static char[] toChars(byte[] value) { char[] dst = new char[value.length >> 1]; getChars(value, 0, dst.length, dst, 0); @@ -162,7 +189,7 @@ @HotSpotIntrinsicCandidate public static int compress(byte[] src, int srcOff, byte[] dst, int dstOff, int len) { // We need a range check here because 'getChar' has no checks - checkBoundsOffCount(srcOff << 1, len << 1, src.length); + checkBoundsOffCount(srcOff, len, src); for (int i = 0; i < len; i++) { char c = getChar(src, srcOff); if (c > 0xFF) { @@ -212,7 +239,7 @@ public static void getChars(byte[] value, int srcBegin, int srcEnd, char dst[], int dstBegin) { // We need a range check here because 'getChar' has no checks if (srcBegin < srcEnd) { - checkBoundsOffCount(srcBegin << 1, (srcEnd - srcBegin) << 1, value.length); + checkBoundsOffCount(srcBegin, srcEnd - srcBegin, value); } for (int i = srcBegin; i < srcEnd; i++) { dst[dstBegin++] = getChar(value, i); @@ -319,14 +346,25 @@ if (str.length == 0) { return 0; } - if (value.length == 0) { + if (value.length < str.length) { return -1; } - return indexOf(value, length(value), str, length(str), 0); + return indexOfUnsafe(value, length(value), str, length(str), 0); } @HotSpotIntrinsicCandidate public static int indexOf(byte[] value, int valueCount, byte[] str, int strCount, int fromIndex) { + checkBoundsBeginEnd(fromIndex, valueCount, value); + checkBoundsBeginEnd(0, strCount, str); + return indexOfUnsafe(value, valueCount, str, strCount, fromIndex); + } + + + private static int indexOfUnsafe(byte[] value, int valueCount, byte[] str, int strCount, int fromIndex) { + assert fromIndex >= 0; + assert strCount > 0; + assert strCount <= length(str); + assert valueCount >= strCount; char first = getChar(str, 0); int max = (valueCount - strCount); for (int i = fromIndex; i <= max; i++) { @@ -348,6 +386,7 @@ return -1; } + /** * Handles indexOf Latin1 substring in UTF16 string. */ @@ -356,14 +395,24 @@ if (str.length == 0) { return 0; } - if (value.length == 0) { + if (length(value) < str.length) { return -1; } - return indexOfLatin1(value, length(value), str, str.length, 0); + return indexOfLatin1Unsafe(value, length(value), str, str.length, 0); } @HotSpotIntrinsicCandidate public static int indexOfLatin1(byte[] src, int srcCount, byte[] tgt, int tgtCount, int fromIndex) { + checkBoundsBeginEnd(fromIndex, srcCount, src); + String.checkBoundsBeginEnd(0, tgtCount, tgt.length); + return indexOfLatin1Unsafe(src, srcCount, tgt, tgtCount, fromIndex); + } + + public static int indexOfLatin1Unsafe(byte[] src, int srcCount, byte[] tgt, int tgtCount, int fromIndex) { + assert fromIndex >= 0; + assert tgtCount > 0; + assert tgtCount <= tgt.length; + assert srcCount >= tgtCount; char first = (char)(tgt[0] & 0xff); int max = (srcCount - tgtCount); for (int i = fromIndex; i <= max; i++) { @@ -389,6 +438,11 @@ @HotSpotIntrinsicCandidate private static int indexOfChar(byte[] value, int ch, int fromIndex, int max) { + checkBoundsBeginEnd(fromIndex, max, value); + return indexOfCharUnsafe(value, ch, fromIndex, max); + } + + private static int indexOfCharUnsafe(byte[] value, int ch, int fromIndex, int max) { for (int i = fromIndex; i < max; i++) { if (getChar(value, i) == ch) { return i; @@ -404,6 +458,7 @@ if (Character.isValidCodePoint(ch)) { final char hi = Character.highSurrogate(ch); final char lo = Character.lowSurrogate(ch); + checkBoundsBeginEnd(fromIndex, max, value); for (int i = fromIndex; i < max - 1; i++) { if (getChar(value, i) == hi && getChar(value, i + 1 ) == lo) { return i; @@ -413,13 +468,21 @@ return -1; } + // srcCoder == UTF16 && tgtCoder == UTF16 public static int lastIndexOf(byte[] src, int srcCount, byte[] tgt, int tgtCount, int fromIndex) { + assert fromIndex >= 0; + assert tgtCount > 0; + assert tgtCount <= length(tgt); int min = tgtCount - 1; int i = min + fromIndex; int strLastIndex = tgtCount - 1; + + checkIndex(strLastIndex, tgt); char strLastChar = getChar(tgt, strLastIndex); + checkIndex(i, src); + startSearchForLastChar: while (true) { while (i >= min && getChar(src, i) != strLastChar) { @@ -509,6 +572,9 @@ public static boolean regionMatchesCI(byte[] value, int toffset, byte[] other, int ooffset, int len) { int last = toffset + len; + assert toffset >= 0 && ooffset >= 0; + assert ooffset + len <= length(other); + assert last <= length(value); while (toffset < last) { char c1 = getChar(value, toffset++); char c2 = getChar(other, ooffset++); @@ -599,6 +665,8 @@ private static String toLowerCaseEx(String str, byte[] value, byte[] result, int first, Locale locale, boolean localeDependent) { + assert(result.length == value.length); + assert(first >= 0); int resultOffset = first; int length = value.length >> 1; int srcCount; @@ -633,6 +701,8 @@ System.arraycopy(result, 0, result2, 0, resultOffset << 1); result = result2; } + assert resultOffset >= 0; + assert resultOffset + mapLen <= length(result); for (int x = 0; x < mapLen; ++x) { putChar(result, resultOffset++, lowerCharArray[x]); } @@ -697,6 +767,8 @@ byte[] result, int first, Locale locale, boolean localeDependent) { + assert(result.length == value.length); + assert(first >= 0); int resultOffset = first; int length = value.length >> 1; int srcCount; @@ -733,10 +805,12 @@ byte[] result2 = newBytesFor((result.length >> 1) + mapLen - srcCount); System.arraycopy(result, 0, result2, 0, resultOffset << 1); result = result2; - } - for (int x = 0; x < mapLen; ++x) { + } + assert resultOffset >= 0; + assert resultOffset + mapLen <= length(result); + for (int x = 0; x < mapLen; ++x) { putChar(result, resultOffset++, upperCharArray[x]); - } + } } } return newString(result, 0, resultOffset); @@ -757,7 +831,7 @@ null; } - public static void putChars(byte[] val, int index, char[] str, int off, int end) { + private static void putChars(byte[] val, int index, char[] str, int off, int end) { while (off < end) { putChar(val, index++, str[off++]); } @@ -927,35 +1001,172 @@ //////////////////////////////////////////////////////////////// public static void putCharSB(byte[] val, int index, int c) { - checkIndex(index, val.length >> 1); + checkIndex(index, val); putChar(val, index, c); } public static void putCharsSB(byte[] val, int index, char[] ca, int off, int end) { - checkOffset(index + end - off, val.length >> 1); + checkBoundsBeginEnd(index, index + end - off, val); putChars(val, index, ca, off, end); } public static void putCharsSB(byte[] val, int index, CharSequence s, int off, int end) { - checkOffset(index + end - off, val.length >> 1); + checkBoundsBeginEnd(index, index + end - off, val); for (int i = off; i < end; i++) { putChar(val, index++, s.charAt(i)); } } public static int codePointAtSB(byte[] val, int index, int end) { - checkOffset(end, val.length >> 1); - return codePointAt(val, index, end); + return codePointAt(val, index, end, true /* checked */); } public static int codePointBeforeSB(byte[] val, int index) { - checkOffset(index, val.length >> 1); - return codePointBefore(val, index); + return codePointBefore(val, index, true /* checked */); } public static int codePointCountSB(byte[] val, int beginIndex, int endIndex) { - checkOffset(endIndex, val.length >> 1); - return codePointCount(val, beginIndex, endIndex); + return codePointCount(val, beginIndex, endIndex, true /* checked */); + } + + public static int getChars(int i, int begin, int end, byte[] value) { + checkBoundsBeginEnd(begin, end, value); + int pos = getChars(i, end, value); + assert begin == pos; + return pos; + } + + public static int getChars(long l, int begin, int end, byte[] value) { + checkBoundsBeginEnd(begin, end, value); + int pos = getChars(l, end, value); + assert begin == pos; + return pos; + } + + public static boolean contentEquals(byte[] v1, byte[] v2, int len) { + checkBoundsOffCount(0, len, v2); + for (int i = 0; i < len; i++) { + if ((char)(v1[i] & 0xff) != getChar(v2, i)) { + return false; + } + } + return true; + } + + public static boolean contentEquals(byte[] value, CharSequence cs, int len) { + checkOffset(len, value); + for (int i = 0; i < len; i++) { + if (getChar(value, i) != cs.charAt(i)) { + return false; + } + } + return true; + } + + public static int putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4) { + int end = i + 4; + checkBoundsBeginEnd(i, end, value); + putChar(value, i++, c1); + putChar(value, i++, c2); + putChar(value, i++, c3); + putChar(value, i++, c4); + assert(i == end); + return end; + } + + public static int putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4, char c5) { + int end = i + 5; + checkBoundsBeginEnd(i, end, value); + putChar(value, i++, c1); + putChar(value, i++, c2); + putChar(value, i++, c3); + putChar(value, i++, c4); + putChar(value, i++, c5); + assert(i == end); + return end; + } + + public static char charAt(byte[] value, int index) { + checkIndex(index, value); + return getChar(value, index); + } + + public static void reverse(byte[] val, int count) { + checkOffset(count, val); + int n = count - 1; + boolean hasSurrogates = false; + for (int j = (n-1) >> 1; j >= 0; j--) { + int k = n - j; + char cj = getChar(val, j); + char ck = getChar(val, k); + putChar(val, j, ck); + putChar(val, k, cj); + if (Character.isSurrogate(cj) || + Character.isSurrogate(ck)) { + hasSurrogates = true; + } + } + if (hasSurrogates) { + reverseAllValidSurrogatePairs(val, count); + } + } + + /** Outlined helper method for reverse() */ + private static void reverseAllValidSurrogatePairs(byte[] val, int count) { + for (int i = 0; i < count - 1; i++) { + char c2 = getChar(val, i); + if (Character.isLowSurrogate(c2)) { + char c1 = getChar(val, i + 1); + if (Character.isHighSurrogate(c1)) { + putChar(val, i++, c1); + putChar(val, i, c2); + } + } + } + } + + // inflatedCopy byte[] -> byte[] + public static void inflate(byte[] src, int srcOff, byte[] dst, int dstOff, int len) { + // We need a range check here because 'putChar' has no checks + checkBoundsOffCount(dstOff, len, dst); + for (int i = 0; i < len; i++) { + putChar(dst, dstOff++, src[srcOff++] & 0xff); + } + } + + // srcCoder == UTF16 && tgtCoder == LATIN1 + public static int lastIndexOfLatin1(byte[] src, int srcCount, + byte[] tgt, int tgtCount, int fromIndex) { + assert fromIndex >= 0; + assert tgtCount > 0; + assert tgtCount <= tgt.length; + int min = tgtCount - 1; + int i = min + fromIndex; + int strLastIndex = tgtCount - 1; + + char strLastChar = (char)(tgt[strLastIndex] & 0xff); + + checkIndex(i, src); + + startSearchForLastChar: + while (true) { + while (i >= min && getChar(src, i) != strLastChar) { + i--; + } + if (i < min) { + return -1; + } + int j = i - 1; + int start = j - strLastIndex; + int k = strLastIndex - 1; + while (j > start) { + if (getChar(src, j--) != (tgt[k--] & 0xff)) { + i--; + continue startSearchForLastChar; + } + } + return start + 1; + } } //////////////////////////////////////////////////////////////// @@ -975,4 +1186,123 @@ } static final int MAX_LENGTH = Integer.MAX_VALUE >> 1; + + // Used by trusted callers. Assumes all necessary bounds checks have + // been done by the caller. + + /** + * This is a variant of {@link Integer#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 getChars(int i, int index, byte[] buf) { + int q, r; + int charPos = index; + + boolean negative = (i < 0); + if (!negative) { + i = -i; + } + + // Get 2 digits/iteration using ints + while (i <= -100) { + q = i / 100; + r = (q * 100) - i; + i = q; + putChar(buf, --charPos, Integer.DigitOnes[r]); + putChar(buf, --charPos, Integer.DigitTens[r]); + } + + // We know there are at most two digits left at this point. + q = i / 10; + r = (q * 10) - i; + putChar(buf, --charPos, '0' + r); + + // Whatever left is the remaining digit. + if (q < 0) { + putChar(buf, --charPos, '0' - q); + } + + if (negative) { + putChar(buf, --charPos, '-'); + } + return charPos; + } + + /** + * This is a variant of {@link Long#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 getChars(long i, int index, byte[] buf) { + long q; + int r; + int charPos = index; + + boolean negative = (i < 0); + if (!negative) { + i = -i; + } + + // Get 2 digits/iteration using longs until quotient fits into an int + while (i <= Integer.MIN_VALUE) { + q = i / 100; + r = (int)((q * 100) - i); + i = q; + putChar(buf, --charPos, Integer.DigitOnes[r]); + putChar(buf, --charPos, Integer.DigitTens[r]); + } + + // Get 2 digits/iteration using ints + int q2; + int i2 = (int)i; + while (i2 <= -100) { + q2 = i2 / 100; + r = (q2 * 100) - i2; + i2 = q2; + putChar(buf, --charPos, Integer.DigitOnes[r]); + putChar(buf, --charPos, Integer.DigitTens[r]); + } + + // We know there are at most two digits left at this point. + q2 = i2 / 10; + r = (q2 * 10) - i2; + putChar(buf, --charPos, '0' + r); + + // Whatever left is the remaining digit. + if (q2 < 0) { + putChar(buf, --charPos, '0' - q2); + } + + if (negative) { + putChar(buf, --charPos, '-'); + } + return charPos; + } + // End of trusted methods. + + public static void checkIndex(int off, byte[] val) { + String.checkIndex(off, length(val)); + } + + public static void checkOffset(int off, byte[] val) { + String.checkOffset(off, length(val)); + } + + public static void checkBoundsBeginEnd(int begin, int end, byte[] val) { + String.checkBoundsBeginEnd(begin, end, length(val)); + } + + public static void checkBoundsOffCount(int offset, int count, byte[] val) { + String.checkBoundsOffCount(offset, count, length(val)); + } + } diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/src/java.base/unix/classes/java/lang/ProcessImpl.java --- a/jdk/src/java.base/unix/classes/java/lang/ProcessImpl.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/src/java.base/unix/classes/java/lang/ProcessImpl.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -617,7 +617,7 @@ } @Override - public long getPid() { + public long pid() { return pid; } diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/src/java.base/windows/classes/java/lang/ProcessImpl.java --- a/jdk/src/java.base/windows/classes/java/lang/ProcessImpl.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/src/java.base/windows/classes/java/lang/ProcessImpl.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -523,7 +523,7 @@ @Override public CompletableFuture onExit() { - return ProcessHandleImpl.completion(getPid(), false) + return ProcessHandleImpl.completion(pid(), false) .handleAsync((exitStatus, unusedThrowable) -> this); } @@ -550,8 +550,8 @@ private static native void terminateProcess(long handle); @Override - public long getPid() { - return processHandle.getPid(); + public long pid() { + return processHandle.pid(); } private static native int getProcessId0(long handle); @@ -572,7 +572,7 @@ @Override public String toString() { int exitCode = getExitCodeProcess(handle); - return new StringBuilder("Process[pid=").append(getPid()) + return new StringBuilder("Process[pid=").append(pid()) .append(", exitValue=").append(exitCode == STILL_ACTIVE ? "\"not exited\"" : exitCode) .append("]").toString(); } diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Exchange.java --- a/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Exchange.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Exchange.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ package jdk.incubator.http; import java.io.IOException; +import java.io.UncheckedIOException; import java.net.InetSocketAddress; import java.net.ProxySelector; import java.net.SocketPermission; @@ -64,6 +65,9 @@ final HttpRequestImpl request; final HttpClientImpl client; volatile ExchangeImpl exchImpl; + // used to record possible cancellation raised before the exchImpl + // has been established. + private volatile IOException failed; final List permissions = new LinkedList<>(); final AccessControlContext acc; final MultiExchange multi; @@ -143,14 +147,80 @@ } public void cancel() { + // cancel can be called concurrently before or at the same time + // that the exchange impl is being established. + // In that case we won't be able to propagate the cancellation + // right away if (exchImpl != null) { exchImpl.cancel(); + } else { + // no impl - can't cancel impl yet. + // call cancel(IOException) instead which takes care + // of race conditions between impl/cancel. + cancel(new IOException("Request cancelled")); } } public void cancel(IOException cause) { - if (exchImpl != null) { - exchImpl.cancel(cause); + // If the impl is non null, propagate the exception right away. + // Otherwise record it so that it can be propagated once the + // exchange impl has been established. + ExchangeImpl impl = exchImpl; + if (impl != null) { + // propagate the exception to the impl + impl.cancel(cause); + } else { + try { + // no impl yet. record the exception + failed = cause; + // now call checkCancelled to recheck the impl. + // if the failed state is set and the impl is not null, reset + // the failed state and propagate the exception to the impl. + checkCancelled(false); + } catch (IOException x) { + // should not happen - we passed 'false' above + throw new UncheckedIOException(x); + } + } + } + + // This method will raise an exception if one was reported and if + // it is possible to do so. If the exception can be raised, then + // the failed state will be reset. Otherwise, the failed state + // will persist until the exception can be raised and the failed state + // can be cleared. + // Takes care of possible race conditions. + private void checkCancelled(boolean throwIfNoImpl) throws IOException { + ExchangeImpl impl = null; + IOException cause = null; + if (failed != null) { + synchronized(this) { + cause = failed; + impl = exchImpl; + if (throwIfNoImpl || impl != null) { + // The exception will be raised by one of the two methods + // below: reset the failed state. + failed = null; + } + } + } + if (cause == null) return; + if (impl != null) { + // The exception is raised by propagating it to the impl. + impl.cancel(cause); + } else if (throwIfNoImpl) { + // The exception is raised by throwing it immediately + throw cause; + } else { + Log.logTrace("Exchange: request [{0}/timeout={1}ms] no impl is set." + + "\n\tCan''t cancel yet with {2}", + request.uri(), + request.duration() == null ? -1 : + // calling duration.toMillis() can throw an exception. + // this is just debugging, we don't care if it overflows. + (request.duration().getSeconds() * 1000 + + request.duration().getNano() / 1000000), + cause); } } @@ -191,10 +261,26 @@ } } + // get/set the exchange impl, solving race condition issues with + // potential concurrent calls to cancel() or cancel(IOException) + private void establishExchange(HttpConnection connection) + throws IOException, InterruptedException + { + // check if we have been cancelled first. + checkCancelled(true); + // not yet cancelled: create/get a new impl + exchImpl = ExchangeImpl.get(this, connection); + // recheck for cancelled, in case of race conditions + checkCancelled(true); + // now we're good to go. because exchImpl is no longer null + // cancel() will be able to propagate directly to the impl + // after this point. + } + private Response responseImpl0(HttpConnection connection) throws IOException, InterruptedException { - exchImpl = ExchangeImpl.get(this, connection); + establishExchange(connection); exchImpl.setClientForRequest(requestProcessor); if (request.expectContinue()) { Log.logTrace("Sending Expect: 100-Continue"); @@ -257,7 +343,7 @@ CompletableFuture responseAsyncImpl0(HttpConnection connection) { try { - exchImpl = ExchangeImpl.get(this, connection); + establishExchange(connection); } catch (IOException | InterruptedException e) { return MinimalFuture.failedFuture(e); } diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1Exchange.java --- a/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1Exchange.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1Exchange.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -46,9 +46,12 @@ class Http1Exchange extends ExchangeImpl { final HttpRequestImpl request; // main request - final List> operations; // used for cancel + private final List> operations; // used for cancel final Http1Request requestAction; - volatile Http1Response response; + private volatile Http1Response response; + // use to record possible cancellation raised before any operation + // has been initiated. + private IOException failed; final HttpConnection connection; final HttpClientImpl client; final Executor executor; @@ -70,7 +73,7 @@ this.request = exchange.request(); this.client = exchange.client(); this.executor = exchange.executor(); - this.operations = Collections.synchronizedList(new LinkedList<>()); + this.operations = new LinkedList<>(); this.buffer = exchange.getBuffer(); if (connection != null) { this.connection = connection; @@ -186,9 +189,22 @@ } connection.close(); int count = 0; - for (CompletableFuture cf : operations) { - cf.completeExceptionally(cause); - count++; + if (operations.isEmpty()) { + failed = cause; + Log.logTrace("Http1Exchange: request [{0}/timeout={1}ms] no pending operation." + + "\n\tCan''t cancel yet with {2}", + request.uri(), + request.duration() == null ? -1 : + // calling duration.toMillis() can throw an exception. + // this is just debugging, we don't care if it overflows. + (request.duration().getSeconds() * 1000 + + request.duration().getNano() / 1000000), + cause); + } else { + for (CompletableFuture cf : operations) { + cf.completeExceptionally(cause); + count++; + } } Log.logError("Http1Exchange.cancel: count=" + count); } @@ -206,8 +222,24 @@ CompletableFuture cf = connection.whenReceivingResponse() .thenCompose((v) -> getResponseAsyncImpl(executor)); - - operations.add(cf); + IOException cause; + synchronized(this) { + operations.add(cf); + cause = failed; + failed = null; + } + if (cause != null) { + Log.logTrace("Http1Exchange: request [{0}/timeout={1}ms]" + + "\n\tCompleting exceptionally with {2}\n", + request.uri(), + request.duration() == null ? -1 : + // calling duration.toMillis() can throw an exception. + // this is just debugging, we don't care if it overflows. + (request.duration().getSeconds() * 1000 + + request.duration().getNano() / 1000000), + cause); + cf.completeExceptionally(cause); + } return cf; } } diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientImpl.java --- a/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientImpl.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientImpl.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -582,12 +582,14 @@ // Timers are implemented through timed Selector.select() calls. synchronized void registerTimer(TimeoutEvent event) { + Log.logTrace("Registering timer {0}", event); timeouts.add(event); selmgr.wakeupSelector(); } synchronized void cancelTimer(TimeoutEvent event) { - timeouts.stream().filter(e -> e == event).forEach(timeouts::remove); + Log.logTrace("Canceling timer {0}", event); + timeouts.remove(event); } /** @@ -595,23 +597,61 @@ * returns the amount of time, in milliseconds, until the next earliest * event. A return value of 0 means that there are no events. */ - private synchronized long purgeTimeoutsAndReturnNextDeadline() { - if (timeouts.isEmpty()) - return 0L; + private long purgeTimeoutsAndReturnNextDeadline() { + long diff = 0L; + List toHandle = null; + int remaining = 0; + // enter critical section to retrieve the timeout event to handle + synchronized(this) { + if (timeouts.isEmpty()) return 0L; + + Instant now = Instant.now(); + Iterator itr = timeouts.iterator(); + while (itr.hasNext()) { + TimeoutEvent event = itr.next(); + diff = now.until(event.deadline(), ChronoUnit.MILLIS); + if (diff <= 0) { + itr.remove(); + toHandle = (toHandle == null) ? new ArrayList<>() : toHandle; + toHandle.add(event); + } else { + break; + } + } + remaining = timeouts.size(); + } - Instant now = Instant.now(); - Iterator itr = timeouts.iterator(); - while (itr.hasNext()) { - TimeoutEvent event = itr.next(); - long diff = now.until(event.deadline(), ChronoUnit.MILLIS); - if (diff <= 0) { - itr.remove(); - event.handle(); // TODO: release lock. - } else { - return diff; + // can be useful for debugging + if (toHandle != null && Log.trace()) { + Log.logTrace("purgeTimeoutsAndReturnNextDeadline: handling " + + (toHandle == null ? 0 : toHandle.size()) + " events, " + + "remaining " + remaining + + ", next deadline: " + (diff < 0 ? 0L : diff)); + } + + // handle timeout events out of critical section + if (toHandle != null) { + Throwable failed = null; + for (TimeoutEvent event : toHandle) { + try { + Log.logTrace("Firing timer {0}", event); + event.handle(); + } catch (Error | RuntimeException e) { + // Not expected. Handle remaining events then throw... + // If e is an OOME or SOE it might simply trigger a new + // error from here - but in this case there's not much we + // could do anyway. Just let it flow... + if (failed == null) failed = e; + else failed.addSuppressed(e); + Log.logTrace("Failed to handle event {0}: {1}", event, e); + } } + if (failed instanceof Error) throw (Error) failed; + if (failed instanceof RuntimeException) throw (RuntimeException) failed; } - return 0L; + + // return time to wait until next event. 0L if there's no more events. + return diff < 0 ? 0L : diff; } // used for the connection window diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/MultiExchange.java --- a/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/MultiExchange.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/MultiExchange.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -357,9 +357,5 @@ public void handle() { cancel(new HttpTimeoutException("request timed out")); } - @Override - public String toString() { - return "[deadline = " + deadline() + "]"; - } } } diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainHttpConnection.java --- a/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainHttpConnection.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainHttpConnection.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -54,7 +54,7 @@ */ class PlainHttpConnection extends HttpConnection implements AsyncConnection { - protected SocketChannel chan; + protected final SocketChannel chan; private volatile boolean connected; private boolean closed; @@ -100,6 +100,7 @@ chan.finishConnect(); } catch (IOException e) { cf.completeExceptionally(e); + return; } connected = true; cf.complete(null); diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Stream.java --- a/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Stream.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Stream.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -670,11 +670,21 @@ @Override CompletableFuture getResponseAsync(Executor executor) { - CompletableFuture cf; + CompletableFuture cf = null; + // The code below deals with race condition that can be caused when + // completeResponse() is being called before getResponseAsync() synchronized (response_cfs) { if (!response_cfs.isEmpty()) { + // This CompletableFuture was created by completeResponse(). + // it will be already completed. cf = response_cfs.remove(0); + // if we find a cf here it should be already completed. + // finding a non completed cf should not happen. just assert it. + assert cf.isDone() : "Removing uncompleted response: could cause code to hang!"; } else { + // getResponseAsync() is called first. Create a CompletableFuture + // that will be completed by completeResponse() when + // completeResponse() is called. cf = new MinimalFuture<>(); response_cfs.add(cf); } @@ -708,7 +718,7 @@ cf.complete(resp); response_cfs.remove(cf); return; - } + } // else we found the previous response: just leave it alone. } cf = MinimalFuture.completedFuture(resp); Log.logTrace("Created completed future (streamid={0}): {1}", @@ -742,10 +752,13 @@ */ void completeResponseExceptionally(Throwable t) { synchronized (response_cfs) { - for (CompletableFuture cf : response_cfs) { + // use index to avoid ConcurrentModificationException + // caused by removing the CF from within the loop. + for (int i = 0; i < response_cfs.size(); i++) { + CompletableFuture cf = response_cfs.get(i); if (!cf.isDone()) { cf.completeExceptionally(t); - response_cfs.remove(cf); + response_cfs.remove(i); return; } } diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/TimeoutEvent.java --- a/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/TimeoutEvent.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/TimeoutEvent.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,10 +27,11 @@ import java.time.Duration; import java.time.Instant; +import java.util.concurrent.atomic.AtomicLong; /** * Timeout event notified by selector thread. Executes the given handler if - * the timer not cancelled first. + * the timer not canceled first. * * Register with {@link HttpClientImpl#registerTimer(TimeoutEvent)}. * @@ -38,6 +39,10 @@ */ abstract class TimeoutEvent implements Comparable { + private static final AtomicLong COUNTER = new AtomicLong(); + // we use id in compareTo to make compareTo consistent with equals + // see TimeoutEvent::compareTo below; + private final long id = COUNTER.incrementAndGet(); private final Instant deadline; TimeoutEvent(Duration duration) { @@ -52,6 +57,24 @@ @Override public int compareTo(TimeoutEvent other) { - return this.deadline.compareTo(other.deadline); + if (other == this) return 0; + // if two events have the same deadline, but are not equals, then the + // smaller is the one that was created before (has the smaller id). + // This is arbitrary and we don't really care which is smaller or + // greater, but we need a total order, so two events with the + // same deadline cannot compare == 0 if they are not equals. + final int compareDeadline = this.deadline.compareTo(other.deadline); + if (compareDeadline == 0 && !this.equals(other)) { + long diff = this.id - other.id; // should take care of wrap around + if (diff < 0) return -1; + else if (diff > 0) return 1; + else assert false : "Different events with same id and deadline"; + } + return compareDeadline; + } + + @Override + public String toString() { + return "TimeoutEvent[id=" + id + ", deadline=" + deadline + "]"; } } diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/src/jdk.management/share/classes/com/sun/management/VMOption.java --- a/jdk/src/jdk.management/share/classes/com/sun/management/VMOption.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/src/jdk.management/share/classes/com/sun/management/VMOption.java Thu Apr 13 16:27:18 2017 +0000 @@ -47,8 +47,6 @@ * object was constructed. The value of the VM option * may be changed after the {@code VMOption} object was constructed, * - * @see - * Java Virtual Machine * @author Mandy Chung * @since 1.6 */ diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/test/ProblemList.txt --- a/jdk/test/ProblemList.txt Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/test/ProblemList.txt Thu Apr 13 16:27:18 2017 +0000 @@ -170,8 +170,6 @@ java/net/DatagramSocket/SendDatagramToBadAddress.java 7143960 macosx-all -java/net/httpclient/TimeoutOrdering.java 8170940 solaris-all,windows-all - ############################################################################ # jdk_nio diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/test/com/sun/jdi/ProcessAttachTest.java --- a/jdk/test/com/sun/jdi/ProcessAttachTest.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/test/com/sun/jdi/ProcessAttachTest.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,13 +23,11 @@ import java.io.IOException; import java.io.InputStream; -import java.util.List; import java.util.Map; import jdk.testlibrary.ProcessTools; import com.sun.jdi.Bootstrap; -import com.sun.jdi.ThreadReference; import com.sun.jdi.VirtualMachine; import com.sun.jdi.connect.AttachingConnector; import com.sun.jdi.connect.Connector; @@ -86,7 +84,7 @@ is.read(); // Attach a debugger - tryDebug(p.getPid()); + tryDebug(p.pid()); } finally { p.destroyForcibly(); } diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/test/java/io/FilePermission/Correctness.java --- a/jdk/test/java/io/FilePermission/Correctness.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/test/java/io/FilePermission/Correctness.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -89,12 +89,14 @@ //check("/-", "-"); try { - // containsPath is broken on Windows. containsMethod = FilePermission.class.getDeclaredMethod( "containsPath", Path.class, Path.class); containsMethod.setAccessible(true); System.out.println(); + // The 1st 2 args of contains() must be normalized paths. + // When FilePermission::containsPath is called by implies, + // paths have already been normalized. contains("x", "x", 0); contains("x", "x/y", 1); contains("x", "x/y/z", 2); @@ -160,7 +162,7 @@ } } - static void check(String s1, String s2, boolean expected) { + static void check0(String s1, String s2, boolean expected) { FilePermission fp1 = new FilePermission(s1, "read"); FilePermission fp2 = new FilePermission(s2, "read"); boolean b = fp1.implies(fp2); @@ -173,6 +175,16 @@ } } + static void check(String s1, String s2, boolean expected) { + check0(s1, s2, expected); + if (isWindows) { + check0("C:" + s1, s2, false); + check0(s1, "C:" + s2, false); + check0("C:" + s1, "D:" + s2, false); + check0("C:" + s1, "C:" + s2, expected); + } + } + static void check(String s1, String s2) { check(s1, s2, true); } diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/test/java/lang/ProcessBuilder/Basic.java --- a/jdk/test/java/lang/ProcessBuilder/Basic.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/test/java/lang/ProcessBuilder/Basic.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -311,7 +311,7 @@ if (action.equals("sleep")) { Thread.sleep(10 * 60 * 1000L); } else if (action.equals("pid")) { - System.out.println(ProcessHandle.current().getPid()); + System.out.println(ProcessHandle.current().pid()); } else if (action.equals("testIO")) { String expected = "standard input"; char[] buf = new char[expected.length()+1]; @@ -1235,7 +1235,7 @@ Process p = pb.start(); String s = commandOutput(p); long actualPid = Long.valueOf(s.trim()); - long expectedPid = p.getPid(); + long expectedPid = p.pid(); equal(actualPid, expectedPid); } catch (Throwable t) { unexpected(t); @@ -1245,7 +1245,7 @@ // Test the default implementation of Process.getPid DelegatingProcess p = new DelegatingProcess(null); THROWS(UnsupportedOperationException.class, - () -> p.getPid(), + () -> p.pid(), () -> p.toHandle(), () -> p.supportsNormalTermination(), () -> p.children(), @@ -2243,7 +2243,7 @@ // Child process waits until it gets input String s = p.toString(); check(s.contains("not exited")); - check(s.contains("pid=" + p.getPid() + ",")); + check(s.contains("pid=" + p.pid() + ",")); new PrintStream(p.getOutputStream()).print("standard input"); p.getOutputStream().close(); @@ -2251,7 +2251,7 @@ // Check the toString after it exits int exitValue = p.waitFor(); s = p.toString(); - check(s.contains("pid=" + p.getPid() + ",")); + check(s.contains("pid=" + p.pid() + ",")); check(s.contains("exitValue=" + exitValue) && !s.contains("not exited")); } diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/test/java/lang/ProcessBuilder/PipelineTest.java --- a/jdk/test/java/lang/ProcessBuilder/PipelineTest.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/test/java/lang/ProcessBuilder/PipelineTest.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -212,7 +212,7 @@ static void print(ProcessHandle p) { System.out.printf("process: pid: %d, info: %s%n", - p.getPid(), p.info()); + p.pid(), p.info()); } // Check various aspects of the processes diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/test/java/lang/ProcessBuilder/Zombies.java --- a/jdk/test/java/lang/ProcessBuilder/Zombies.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/test/java/lang/ProcessBuilder/Zombies.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,7 +43,7 @@ ! new File("/bin/ps").canExecute()) return; System.out.println("Looks like a Unix system."); - long mypid = ProcessHandle.current().getPid(); + long mypid = ProcessHandle.current().pid(); System.out.printf("mypid: %d%n", mypid); final Runtime rt = Runtime.getRuntime(); @@ -65,7 +65,7 @@ Process p = rt.exec(TrueCommand); ProcessHandle pp = p.toHandle().parent().orElse(null); - System.out.printf("%s pid: %d, parent: %s%n", TrueCommand, p.getPid(), pp); + System.out.printf("%s pid: %d, parent: %s%n", TrueCommand, p.pid(), pp); p.waitFor(); // Count all the zombies that are children of this Java process diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/test/java/lang/ProcessHandle/Basic.java --- a/jdk/test/java/lang/ProcessHandle/Basic.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/test/java/lang/ProcessHandle/Basic.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -66,13 +66,13 @@ public static void test2() { try { ProcessHandle self = ProcessHandle.current(); - long pid = self.getPid(); // known native process id + long pid = self.pid(); // known native process id Optional self1 = ProcessHandle.of(pid); assertEquals(self1.get(), self, - "ProcessHandle.of(x.getPid()) should be equal getPid() %d: %d"); + "ProcessHandle.of(x.pid()) should be equal pid() %d: %d"); Optional ph = ProcessHandle.of(pid); - assertEquals(pid, ph.get().getPid()); + assertEquals(pid, ph.get().pid()); } finally { // Cleanup any left over processes ProcessHandle.current().children().forEach(ProcessHandle::destroy); @@ -98,7 +98,7 @@ Process p = new ProcessBuilder("sleep", "0").start(); p.waitFor(); - long deadPid = p.getPid(); + long deadPid = p.pid(); p = null; // Forget the process Optional t = ProcessHandle.of(deadPid); diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/test/java/lang/ProcessHandle/JavaChild.java --- a/jdk/test/java/lang/ProcessHandle/JavaChild.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/test/java/lang/ProcessHandle/JavaChild.java Thu Apr 13 16:27:18 2017 +0000 @@ -159,7 +159,7 @@ */ CompletableFuture forEachOutputLine(Consumer consumer) { final CompletableFuture future = new CompletableFuture<>(); - String name = "OutputLineReader-" + getPid(); + String name = "OutputLineReader-" + pid(); Thread t = new Thread(() -> { try (BufferedReader reader = outputReader()) { String line; @@ -167,7 +167,7 @@ consumer.accept(line); } } catch (IOException | RuntimeException ex) { - consumer.accept("IOE (" + getPid() + "):" + ex.getMessage()); + consumer.accept("IOE (" + pid() + "):" + ex.getMessage()); future.completeExceptionally(ex); } future.complete("success"); @@ -327,7 +327,7 @@ try { p.getOutputStream().close(); } catch (IOException ie) { - sendResult("stdin_closing", p.getPid(), + sendResult("stdin_closing", p.pid(), "exception", ie.getMessage()); } } @@ -352,9 +352,9 @@ "children to terminate%n"); children.removeAll(completedChildren); for (JavaChild c : children) { - sendResult("stdin_noterm", c.getPid()); + sendResult("stdin_noterm", c.pid()); System.err.printf(" Process not terminated: " + - "pid: %d%n", c.getPid()); + "pid: %d%n", c.pid()); } System.exit(2); } @@ -386,11 +386,11 @@ System.arraycopy(args, nextArg, subargs, 0, subargs.length); for (int i = 0; i < ncount; i++) { JavaChild p = spawnJavaChild(subargs); - sendResult(action, p.getPid()); + sendResult(action, p.pid()); p.forEachOutputLine(JavaChild::sendRaw); p.onJavaChildExit().thenAccept((p1) -> { int excode = p1.exitValue(); - sendResult("child_exit", p1.getPid(), excode); + sendResult("child_exit", p1.pid(), excode); completedChildren.add(p1); }); children.add(p); // Add child to spawned list @@ -410,7 +410,7 @@ if (p.isAlive()) { sentCount++; // overwrite with current pid - result[0] = Long.toString(p.getPid()); + result[0] = Long.toString(p.pid()); sendResult(action, result); p.sendAction(args[nextArg], subargs); } @@ -426,7 +426,7 @@ // ignoring those that are not alive for (JavaChild p : children) { if (p.isAlive()) { - sendResult(action, p.getPid()); + sendResult(action, p.pid()); p.getOutputStream().close(); } } @@ -505,7 +505,7 @@ String command; Object[] results; Event(String command, Object... results) { - this(self.getPid(), ++commandSeq, command, results); + this(self.pid(), ++commandSeq, command, results); } Event(long pid, int seq, String command, Object... results) { this.pid = pid; diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/test/java/lang/ProcessHandle/OnExitTest.java --- a/jdk/test/java/lang/ProcessHandle/OnExitTest.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/test/java/lang/ProcessHandle/OnExitTest.java Thu Apr 13 16:27:18 2017 +0000 @@ -104,7 +104,7 @@ JavaChild proc = JavaChild.spawnJavaChild("stdin"); procHandle = proc.toHandle(); - printf(" spawned: %d%n", proc.getPid()); + printf(" spawned: %d%n", proc.pid()); proc.forEachOutputLine((s) -> { String[] split = s.trim().split(" "); @@ -235,7 +235,7 @@ } while (!"pid".equals(split[1])); // Tell B to wait for A's pid - B.sendAction("waitpid", A.getPid()); + B.sendAction("waitpid", A.pid()); // Wait a bit to see if B will prematurely report the termination of A try { diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/test/java/lang/ProcessHandle/ProcessUtil.java --- a/jdk/test/java/lang/ProcessHandle/ProcessUtil.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/test/java/lang/ProcessHandle/ProcessUtil.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -199,7 +199,7 @@ */ static void printProcess(ProcessHandle ph, String prefix) { printf("%spid %s, alive: %s; parent: %s, %s%n", prefix, - ph.getPid(), ph.isAlive(), ph.parent(), ph.info()); + ph.pid(), ph.isAlive(), ph.parent(), ph.info()); } /** diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/test/java/lang/ProcessHandle/TreeTest.java --- a/jdk/test/java/lang/ProcessHandle/TreeTest.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/test/java/lang/ProcessHandle/TreeTest.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -73,7 +73,7 @@ try { ProcessHandle self = ProcessHandle.current(); - printf("self pid: %d%n", self.getPid()); + printf("self pid: %d%n", self.pid()); printDeep(self, ""); for (int i = 0; i < MAXCHILDREN; i++) { @@ -154,7 +154,7 @@ JavaChild p1 = JavaChild.spawnJavaChild("stdin"); ProcessHandle p1Handle = p1.toHandle(); - printf(" p1 pid: %d%n", p1.getPid()); + printf(" p1 pid: %d%n", p1.pid()); // Gather the PIDs from the output of the spawing process p1.forEachOutputLine((s) -> { @@ -206,7 +206,7 @@ // show the complete list of children (for debug) List descendants = getDescendants(p1Handle); printf(" descendants: %s%n", - descendants.stream().map(p -> p.getPid()) + descendants.stream().map(p -> p.pid()) .collect(Collectors.toList())); // Verify that all spawned children show up in the descendants List @@ -252,7 +252,7 @@ JavaChild p1 = JavaChild.spawnJavaChild("stdin"); ProcessHandle p1Handle = p1.toHandle(); - printf(" p1: %s%n", p1.getPid()); + printf(" p1: %s%n", p1.pid()); int newChildren = 3; CountDownLatch spawnCount = new CountDownLatch(newChildren); @@ -356,11 +356,11 @@ parent[sortindex[i]] = processes[sortindex[i]].parent().orElse(null); } Arrays.sort(sortindex, (i1, i2) -> { - int cmp = Long.compare((parent[i1] == null ? 0L : parent[i1].getPid()), - (parent[i2] == null ? 0L : parent[i2].getPid())); + int cmp = Long.compare((parent[i1] == null ? 0L : parent[i1].pid()), + (parent[i2] == null ? 0L : parent[i2].pid())); if (cmp == 0) { - cmp = Long.compare((processes[i1] == null ? 0L : processes[i1].getPid()), - (processes[i2] == null ? 0L : processes[i2].getPid())); + cmp = Long.compare((processes[i1] == null ? 0L : processes[i1].pid()), + (processes[i2] == null ? 0L : processes[i2].pid())); } return cmp; }); @@ -397,7 +397,7 @@ ProcessHandle p1Handle = p1.toHandle(); printf("Spawning %d x %d x %d processes, pid: %d%n", - factor, factor, factor, p1.getPid()); + factor, factor, factor, p1.pid()); // Start the first tier of subprocesses p1.sendAction("spawn", factor, "stdin"); @@ -448,7 +448,7 @@ List subprocesses = getDescendants(p1Handle); printf(" descendants: %s%n", - subprocesses.stream().map(p -> p.getPid()) + subprocesses.stream().map(p -> p.pid()) .collect(Collectors.toList())); p1.getOutputStream().close(); // Close stdin for the controlling p1 diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/test/java/lang/Runtime/exec/SleepyCat.java --- a/jdk/test/java/lang/Runtime/exec/SleepyCat.java Thu Apr 13 16:01:13 2017 +0000 +++ b/jdk/test/java/lang/Runtime/exec/SleepyCat.java Thu Apr 13 16:27:18 2017 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -80,10 +80,10 @@ for (Process p : pids) { if (p == null) continue; - String[] pfiles = {"pfiles", Long.toString(p.getPid())}; + String[] pfiles = {"pfiles", Long.toString(p.pid())}; fds = new ProcessBuilder(pfiles).inheritIO().start(); fds.waitFor(); - String[] pstack = {"pstack", Long.toString(p.getPid())}; + String[] pstack = {"pstack", Long.toString(p.pid())}; fds = new ProcessBuilder(pstack).inheritIO().start(); fds.waitFor(); } diff -r ddaea4f20a7b -r 2c3a10f7a923 jdk/test/java/net/httpclient/SmallTimeout.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/net/httpclient/SmallTimeout.java Thu Apr 13 16:27:18 2017 +0000 @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.net.ServerSocket; +import java.net.URI; +import jdk.incubator.http.HttpClient; +import jdk.incubator.http.HttpRequest; +import jdk.incubator.http.HttpResponse; +import jdk.incubator.http.HttpTimeoutException; +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import static java.lang.System.out; +import static jdk.incubator.http.HttpResponse.BodyHandler.discard; + +/** + * @test + * @bug 8178147 + * @summary Ensures that small timeouts do not cause hangs due to race conditions + * @run main/othervm SmallTimeout + */ + +// To enable logging use. Not enabled by default as it changes the dynamics +// of the test. +// @run main/othervm -Djdk.httpclient.HttpClient.log=all,frames:all SmallTimeout + +public class SmallTimeout { + + static int[] TIMEOUTS = {2, 1, 3, 2, 100, 1}; + + // A queue for placing timed out requests so that their order can be checked. + static LinkedBlockingQueue queue = new LinkedBlockingQueue<>(); + + static volatile boolean error; + + public static void main(String[] args) throws Exception { + HttpClient client = HttpClient.newHttpClient(); + + try (ServerSocket ss = new ServerSocket(0, 20)) { + int port = ss.getLocalPort(); + URI uri = new URI("http://127.0.0.1:" + port + "/"); + + HttpRequest[] requests = new HttpRequest[TIMEOUTS.length]; + + out.println("--- TESTING Async"); + for (int i = 0; i < TIMEOUTS.length; i++) { + requests[i] = HttpRequest.newBuilder(uri) + .timeout(Duration.ofMillis(TIMEOUTS[i])) + .GET() + .build(); + + final HttpRequest req = requests[i]; + CompletableFuture> response = client + .sendAsync(req, discard(null)) + .whenComplete((HttpResponse r, Throwable t) -> { + if (r != null) { + out.println("Unexpected response: " + r); + error = true; + } + if (t != null) { + if (!(t.getCause() instanceof HttpTimeoutException)) { + out.println("Wrong exception type:" + t.toString()); + Throwable c = t.getCause() == null ? t : t.getCause(); + c.printStackTrace(); + error = true; + } else { + out.println("Caught expected timeout: " + t.getCause()); + } + } + if (t == null && r == null) { + out.println("Both response and throwable are null!"); + error = true; + } + queue.add(req); + }); + } + System.out.println("All requests submitted. Waiting ..."); + + checkReturn(requests); + + if (error) + throw new RuntimeException("Failed. Check output"); + + // Repeat blocking in separate threads. Use queue to wait. + out.println("--- TESTING Sync"); + + // For running blocking response tasks + ExecutorService executor = Executors.newCachedThreadPool(); + + for (int i = 0; i < TIMEOUTS.length; i++) { + requests[i] = HttpRequest.newBuilder(uri) + .timeout(Duration.ofMillis(TIMEOUTS[i])) + .GET() + .build(); + + final HttpRequest req = requests[i]; + executor.execute(() -> { + try { + client.send(req, discard(null)); + } catch (HttpTimeoutException e) { + out.println("Caught expected timeout: " + e); + queue.offer(req); + } catch (IOException | InterruptedException ee) { + Throwable c = ee.getCause() == null ? ee : ee.getCause(); + c.printStackTrace(); + error = true; + } + }); + } + System.out.println("All requests submitted. Waiting ..."); + + checkReturn(requests); + + executor.shutdownNow(); + + if (error) + throw new RuntimeException("Failed. Check output"); + + } finally { + ((ExecutorService) client.executor()).shutdownNow(); + } + } + + static void checkReturn(HttpRequest[] requests) throws InterruptedException { + // wait for exceptions and check order + for (int j = 0; j < TIMEOUTS.length; j++) { + HttpRequest req = queue.take(); + out.println("Got request from queue " + req + ", order: " + getRequest(req, requests)); + } + out.println("Return ok"); + } + + /** Returns the index of the request in the array. */ + static String getRequest(HttpRequest req, HttpRequest[] requests) { + for (int i=0; i