mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-20 19:14:38 +02:00
Merge
This commit is contained in:
commit
501a6f3423
968 changed files with 30359 additions and 14876 deletions
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2019, 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
|
||||
|
@ -261,6 +261,11 @@ public class LineNumberReader extends BufferedReader {
|
|||
*/
|
||||
public void mark(int readAheadLimit) throws IOException {
|
||||
synchronized (lock) {
|
||||
// If the most recently read character is '\r', then increment the
|
||||
// read ahead limit as in this case if the next character is '\n',
|
||||
// two characters would actually be read by the next read().
|
||||
if (skipLF)
|
||||
readAheadLimit++;
|
||||
super.mark(readAheadLimit);
|
||||
markedLineNumber = lineNumber;
|
||||
markedSkipLF = skipLF;
|
||||
|
|
|
@ -1893,6 +1893,16 @@ public abstract class ClassLoader {
|
|||
* otherwise, if unnamed, it will set the class path to the current
|
||||
* working directory.
|
||||
*
|
||||
* <p> JAR files on the class path may contain a {@code Class-Path} manifest
|
||||
* attribute to specify dependent JAR files to be included in the class path.
|
||||
* {@code Class-Path} entries must meet certain conditions for validity (see
|
||||
* the <a href="{@docRoot}/../specs/jar/jar.html#class-path-attribute">
|
||||
* JAR File Specification</a> for details). Invalid {@code Class-Path}
|
||||
* entries are ignored. For debugging purposes, ignored entries can be
|
||||
* printed to the console if the
|
||||
* {@systemProperty jdk.net.URLClassPath.showIgnoredClassPathEntries} system
|
||||
* property is set to {@code true}.
|
||||
*
|
||||
* @return The system {@code ClassLoader}
|
||||
*
|
||||
* @throws SecurityException
|
||||
|
|
|
@ -1020,9 +1020,8 @@ public final class String
|
|||
}
|
||||
if (anObject instanceof String) {
|
||||
String aString = (String)anObject;
|
||||
if (coder() == aString.coder()) {
|
||||
return isLatin1() ? StringLatin1.equals(value, aString.value)
|
||||
: StringUTF16.equals(value, aString.value);
|
||||
if (!COMPACT_STRINGS || this.coder == aString.coder) {
|
||||
return StringLatin1.equals(value, aString.value);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -1057,7 +1056,8 @@ public final class String
|
|||
}
|
||||
byte v1[] = value;
|
||||
byte v2[] = sb.getValue();
|
||||
if (coder() == sb.getCoder()) {
|
||||
byte coder = coder();
|
||||
if (coder == sb.getCoder()) {
|
||||
int n = v1.length;
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (v1[i] != v2[i]) {
|
||||
|
@ -1065,7 +1065,7 @@ public final class String
|
|||
}
|
||||
}
|
||||
} else {
|
||||
if (!isLatin1()) { // utf16 str and latin1 abs can never be "equal"
|
||||
if (coder != LATIN1) { // utf16 str and latin1 abs can never be "equal"
|
||||
return false;
|
||||
}
|
||||
return StringUTF16.contentEquals(v1, v2, len);
|
||||
|
@ -1209,12 +1209,13 @@ public final class String
|
|||
public int compareTo(String anotherString) {
|
||||
byte v1[] = value;
|
||||
byte v2[] = anotherString.value;
|
||||
if (coder() == anotherString.coder()) {
|
||||
return isLatin1() ? StringLatin1.compareTo(v1, v2)
|
||||
: StringUTF16.compareTo(v1, v2);
|
||||
byte coder = coder();
|
||||
if (coder == anotherString.coder()) {
|
||||
return coder == LATIN1 ? StringLatin1.compareTo(v1, v2)
|
||||
: StringUTF16.compareTo(v1, v2);
|
||||
}
|
||||
return isLatin1() ? StringLatin1.compareToUTF16(v1, v2)
|
||||
: StringUTF16.compareToLatin1(v1, v2);
|
||||
return coder == LATIN1 ? StringLatin1.compareToUTF16(v1, v2)
|
||||
: StringUTF16.compareToLatin1(v1, v2);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1238,12 +1239,13 @@ public final class String
|
|||
public int compare(String s1, String s2) {
|
||||
byte v1[] = s1.value;
|
||||
byte v2[] = s2.value;
|
||||
if (s1.coder() == s2.coder()) {
|
||||
return s1.isLatin1() ? StringLatin1.compareToCI(v1, v2)
|
||||
: StringUTF16.compareToCI(v1, v2);
|
||||
byte coder = s1.coder();
|
||||
if (coder == s2.coder()) {
|
||||
return coder == LATIN1 ? StringLatin1.compareToCI(v1, v2)
|
||||
: StringUTF16.compareToCI(v1, v2);
|
||||
}
|
||||
return s1.isLatin1() ? StringLatin1.compareToCI_UTF16(v1, v2)
|
||||
: StringUTF16.compareToCI_Latin1(v1, v2);
|
||||
return coder == LATIN1 ? StringLatin1.compareToCI_UTF16(v1, v2)
|
||||
: StringUTF16.compareToCI_Latin1(v1, v2);
|
||||
}
|
||||
|
||||
/** Replaces the de-serialized object. */
|
||||
|
@ -1317,7 +1319,8 @@ public final class String
|
|||
(ooffset > (long)other.length() - len)) {
|
||||
return false;
|
||||
}
|
||||
if (coder() == other.coder()) {
|
||||
byte coder = coder();
|
||||
if (coder == other.coder()) {
|
||||
if (!isLatin1() && (len > 0)) {
|
||||
toffset = toffset << 1;
|
||||
ooffset = ooffset << 1;
|
||||
|
@ -1329,7 +1332,7 @@ public final class String
|
|||
}
|
||||
}
|
||||
} else {
|
||||
if (coder() == LATIN1) {
|
||||
if (coder == LATIN1) {
|
||||
while (len-- > 0) {
|
||||
if (StringLatin1.getChar(tv, toffset++) !=
|
||||
StringUTF16.getChar(ov, ooffset++)) {
|
||||
|
@ -1411,12 +1414,13 @@ public final class String
|
|||
}
|
||||
byte tv[] = value;
|
||||
byte ov[] = other.value;
|
||||
if (coder() == other.coder()) {
|
||||
return isLatin1()
|
||||
byte coder = coder();
|
||||
if (coder == other.coder()) {
|
||||
return coder == LATIN1
|
||||
? StringLatin1.regionMatchesCI(tv, toffset, ov, ooffset, len)
|
||||
: StringUTF16.regionMatchesCI(tv, toffset, ov, ooffset, len);
|
||||
}
|
||||
return isLatin1()
|
||||
return coder == LATIN1
|
||||
? StringLatin1.regionMatchesCI_UTF16(tv, toffset, ov, ooffset, len)
|
||||
: StringUTF16.regionMatchesCI_Latin1(tv, toffset, ov, ooffset, len);
|
||||
}
|
||||
|
@ -1447,15 +1451,16 @@ public final class String
|
|||
byte pa[] = prefix.value;
|
||||
int po = 0;
|
||||
int pc = pa.length;
|
||||
if (coder() == prefix.coder()) {
|
||||
int to = isLatin1() ? toffset : toffset << 1;
|
||||
byte coder = coder();
|
||||
if (coder == prefix.coder()) {
|
||||
int to = (coder == LATIN1) ? toffset : toffset << 1;
|
||||
while (po < pc) {
|
||||
if (ta[to++] != pa[po++]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isLatin1()) { // && pcoder == UTF16
|
||||
if (coder == LATIN1) { // && pcoder == UTF16
|
||||
return false;
|
||||
}
|
||||
// coder == UTF16 && pcoder == LATIN1)
|
||||
|
@ -1688,11 +1693,12 @@ public final class String
|
|||
* or {@code -1} if there is no such occurrence.
|
||||
*/
|
||||
public int indexOf(String str) {
|
||||
if (coder() == str.coder()) {
|
||||
byte coder = coder();
|
||||
if (coder == str.coder()) {
|
||||
return isLatin1() ? StringLatin1.indexOf(value, str.value)
|
||||
: StringUTF16.indexOf(value, str.value);
|
||||
}
|
||||
if (coder() == LATIN1) { // str.coder == UTF16
|
||||
if (coder == LATIN1) { // str.coder == UTF16
|
||||
return -1;
|
||||
}
|
||||
return StringUTF16.indexOfLatin1(value, str.value);
|
||||
|
|
|
@ -144,7 +144,7 @@ final class StringConcatHelper {
|
|||
* @param value boolean value to encode
|
||||
* @return updated index (coder value retained)
|
||||
*/
|
||||
static long prepend(long indexCoder, byte[] buf, boolean value) {
|
||||
private static long prepend(long indexCoder, byte[] buf, boolean value) {
|
||||
int index = (int)indexCoder;
|
||||
if (indexCoder < UTF16) {
|
||||
if (value) {
|
||||
|
@ -178,17 +178,41 @@ final class StringConcatHelper {
|
|||
}
|
||||
|
||||
/**
|
||||
* Prepends the stringly representation of byte value into buffer,
|
||||
* Prepends constant and the stringly representation of value into buffer,
|
||||
* given the coder and final index. Index is measured in chars, not in bytes!
|
||||
*
|
||||
* @param indexCoder final char index in the buffer, along with coder packed
|
||||
* into higher bits.
|
||||
* @param buf buffer to append to
|
||||
* @param value byte value to encode
|
||||
* @param prefix a constant to prepend before value
|
||||
* @param value boolean value to encode
|
||||
* @param suffix a constant to prepend after value
|
||||
* @return updated index (coder value retained)
|
||||
*/
|
||||
static long prepend(long indexCoder, byte[] buf, byte value) {
|
||||
return prepend(indexCoder, buf, (int)value);
|
||||
static long prepend(long indexCoder, byte[] buf, String prefix, boolean value, String suffix) {
|
||||
if (suffix != null) indexCoder = prepend(indexCoder, buf, suffix);
|
||||
indexCoder = prepend(indexCoder, buf, value);
|
||||
if (prefix != null) indexCoder = prepend(indexCoder, buf, prefix);
|
||||
return indexCoder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepends constant and the stringly representation of value into buffer,
|
||||
* given the coder and final index. Index is measured in chars, not in bytes!
|
||||
*
|
||||
* @param indexCoder final char index in the buffer, along with coder packed
|
||||
* into higher bits.
|
||||
* @param buf buffer to append to
|
||||
* @param prefix a constant to prepend before value
|
||||
* @param value boolean value to encode
|
||||
* @param suffix a constant to prepend after value
|
||||
* @return updated index (coder value retained)
|
||||
*/
|
||||
static long prepend(long indexCoder, byte[] buf, String prefix, byte value, String suffix) {
|
||||
if (suffix != null) indexCoder = prepend(indexCoder, buf, suffix);
|
||||
indexCoder = prepend(indexCoder, buf, (int)value);
|
||||
if (prefix != null) indexCoder = prepend(indexCoder, buf, prefix);
|
||||
return indexCoder;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -201,7 +225,7 @@ final class StringConcatHelper {
|
|||
* @param value char value to encode
|
||||
* @return updated index (coder value retained)
|
||||
*/
|
||||
static long prepend(long indexCoder, byte[] buf, char value) {
|
||||
private static long prepend(long indexCoder, byte[] buf, char value) {
|
||||
if (indexCoder < UTF16) {
|
||||
buf[(int)(--indexCoder)] = (byte) (value & 0xFF);
|
||||
} else {
|
||||
|
@ -211,17 +235,41 @@ final class StringConcatHelper {
|
|||
}
|
||||
|
||||
/**
|
||||
* Prepends the stringly representation of short value into buffer,
|
||||
* Prepends constant and the stringly representation of value into buffer,
|
||||
* given the coder and final index. Index is measured in chars, not in bytes!
|
||||
*
|
||||
* @param indexCoder final char index in the buffer, along with coder packed
|
||||
* into higher bits.
|
||||
* @param buf buffer to append to
|
||||
* @param value short value to encode
|
||||
* @param prefix a constant to prepend before value
|
||||
* @param value boolean value to encode
|
||||
* @param suffix a constant to prepend after value
|
||||
* @return updated index (coder value retained)
|
||||
*/
|
||||
static long prepend(long indexCoder, byte[] buf, short value) {
|
||||
return prepend(indexCoder, buf, (int)value);
|
||||
static long prepend(long indexCoder, byte[] buf, String prefix, char value, String suffix) {
|
||||
if (suffix != null) indexCoder = prepend(indexCoder, buf, suffix);
|
||||
indexCoder = prepend(indexCoder, buf, value);
|
||||
if (prefix != null) indexCoder = prepend(indexCoder, buf, prefix);
|
||||
return indexCoder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepends constant and the stringly representation of value into buffer,
|
||||
* given the coder and final index. Index is measured in chars, not in bytes!
|
||||
*
|
||||
* @param indexCoder final char index in the buffer, along with coder packed
|
||||
* into higher bits.
|
||||
* @param buf buffer to append to
|
||||
* @param prefix a constant to prepend before value
|
||||
* @param value boolean value to encode
|
||||
* @param suffix a constant to prepend after value
|
||||
* @return updated index (coder value retained)
|
||||
*/
|
||||
static long prepend(long indexCoder, byte[] buf, String prefix, short value, String suffix) {
|
||||
if (suffix != null) indexCoder = prepend(indexCoder, buf, suffix);
|
||||
indexCoder = prepend(indexCoder, buf, (int)value);
|
||||
if (prefix != null) indexCoder = prepend(indexCoder, buf, prefix);
|
||||
return indexCoder;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -234,7 +282,7 @@ final class StringConcatHelper {
|
|||
* @param value integer value to encode
|
||||
* @return updated index (coder value retained)
|
||||
*/
|
||||
static long prepend(long indexCoder, byte[] buf, int value) {
|
||||
private static long prepend(long indexCoder, byte[] buf, int value) {
|
||||
if (indexCoder < UTF16) {
|
||||
return Integer.getChars(value, (int)indexCoder, buf);
|
||||
} else {
|
||||
|
@ -242,6 +290,25 @@ final class StringConcatHelper {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepends constant and the stringly representation of value into buffer,
|
||||
* given the coder and final index. Index is measured in chars, not in bytes!
|
||||
*
|
||||
* @param indexCoder final char index in the buffer, along with coder packed
|
||||
* into higher bits.
|
||||
* @param buf buffer to append to
|
||||
* @param prefix a constant to prepend before value
|
||||
* @param value boolean value to encode
|
||||
* @param suffix a constant to prepend after value
|
||||
* @return updated index (coder value retained)
|
||||
*/
|
||||
static long prepend(long indexCoder, byte[] buf, String prefix, int value, String suffix) {
|
||||
if (suffix != null) indexCoder = prepend(indexCoder, buf, suffix);
|
||||
indexCoder = prepend(indexCoder, buf, value);
|
||||
if (prefix != null) indexCoder = prepend(indexCoder, buf, prefix);
|
||||
return indexCoder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepends the stringly representation of long value into buffer,
|
||||
* given the coder and final index. Index is measured in chars, not in bytes!
|
||||
|
@ -252,7 +319,7 @@ final class StringConcatHelper {
|
|||
* @param value long value to encode
|
||||
* @return updated index (coder value retained)
|
||||
*/
|
||||
static long prepend(long indexCoder, byte[] buf, long value) {
|
||||
private static long prepend(long indexCoder, byte[] buf, long value) {
|
||||
if (indexCoder < UTF16) {
|
||||
return Long.getChars(value, (int)indexCoder, buf);
|
||||
} else {
|
||||
|
@ -260,6 +327,25 @@ final class StringConcatHelper {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepends constant and the stringly representation of value into buffer,
|
||||
* given the coder and final index. Index is measured in chars, not in bytes!
|
||||
*
|
||||
* @param indexCoder final char index in the buffer, along with coder packed
|
||||
* into higher bits.
|
||||
* @param buf buffer to append to
|
||||
* @param prefix a constant to prepend before value
|
||||
* @param value boolean value to encode
|
||||
* @param suffix a constant to prepend after value
|
||||
* @return updated index (coder value retained)
|
||||
*/
|
||||
static long prepend(long indexCoder, byte[] buf, String prefix, long value, String suffix) {
|
||||
if (suffix != null) indexCoder = prepend(indexCoder, buf, suffix);
|
||||
indexCoder = prepend(indexCoder, buf, value);
|
||||
if (prefix != null) indexCoder = prepend(indexCoder, buf, prefix);
|
||||
return indexCoder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepends the stringly representation of String value into buffer,
|
||||
* given the coder and final index. Index is measured in chars, not in bytes!
|
||||
|
@ -270,7 +356,7 @@ final class StringConcatHelper {
|
|||
* @param value String value to encode
|
||||
* @return updated index (coder value retained)
|
||||
*/
|
||||
static long prepend(long indexCoder, byte[] buf, String value) {
|
||||
private static long prepend(long indexCoder, byte[] buf, String value) {
|
||||
indexCoder -= value.length();
|
||||
if (indexCoder < UTF16) {
|
||||
value.getBytes(buf, (int)indexCoder, String.LATIN1);
|
||||
|
@ -280,6 +366,25 @@ final class StringConcatHelper {
|
|||
return indexCoder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepends constant and the stringly representation of value into buffer,
|
||||
* given the coder and final index. Index is measured in chars, not in bytes!
|
||||
*
|
||||
* @param indexCoder final char index in the buffer, along with coder packed
|
||||
* into higher bits.
|
||||
* @param buf buffer to append to
|
||||
* @param prefix a constant to prepend before value
|
||||
* @param value boolean value to encode
|
||||
* @param suffix a constant to prepend after value
|
||||
* @return updated index (coder value retained)
|
||||
*/
|
||||
static long prepend(long indexCoder, byte[] buf, String prefix, String value, String suffix) {
|
||||
if (suffix != null) indexCoder = prepend(indexCoder, buf, suffix);
|
||||
indexCoder = prepend(indexCoder, buf, value);
|
||||
if (prefix != null) indexCoder = prepend(indexCoder, buf, prefix);
|
||||
return indexCoder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates the String with given buffer and coder
|
||||
* @param buf buffer to use
|
||||
|
|
|
@ -193,11 +193,6 @@ class Thread implements Runnable {
|
|||
*/
|
||||
private final long stackSize;
|
||||
|
||||
/*
|
||||
* JVM-private state that persists after native thread termination.
|
||||
*/
|
||||
private long nativeParkEventPointer;
|
||||
|
||||
/*
|
||||
* Thread ID
|
||||
*/
|
||||
|
|
|
@ -72,7 +72,7 @@ public interface ClassDesc
|
|||
*
|
||||
* @param name the fully qualified (dot-separated) binary class name
|
||||
* @return a {@linkplain ClassDesc} describing the desired class
|
||||
* @throws NullPointerException if any argument is {@code null}
|
||||
* @throws NullPointerException if the argument is {@code null}
|
||||
* @throws IllegalArgumentException if the name string is not in the
|
||||
* correct format
|
||||
*/
|
||||
|
@ -123,7 +123,7 @@ public interface ClassDesc
|
|||
*
|
||||
* @param descriptor a field descriptor string
|
||||
* @return a {@linkplain ClassDesc} describing the desired class
|
||||
* @throws NullPointerException if any argument is {@code null}
|
||||
* @throws NullPointerException if the argument is {@code null}
|
||||
* @throws IllegalArgumentException if the name string is not in the
|
||||
* correct format
|
||||
* @jvms 4.3.2 Field Descriptors
|
||||
|
@ -193,7 +193,7 @@ public interface ClassDesc
|
|||
*
|
||||
* @param nestedName the unqualified name of the nested class
|
||||
* @return a {@linkplain ClassDesc} describing the nested class
|
||||
* @throws NullPointerException if any argument is {@code null}
|
||||
* @throws NullPointerException if the argument is {@code null}
|
||||
* @throws IllegalStateException if this {@linkplain ClassDesc} does not
|
||||
* describe a class or interface type
|
||||
* @throws IllegalArgumentException if the nested class name is invalid
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
package java.lang.invoke;
|
||||
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.loader.BootLoader;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.FieldVisitor;
|
||||
|
@ -37,6 +38,7 @@ import java.lang.reflect.Field;
|
|||
import java.lang.reflect.Modifier;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
@ -575,19 +577,19 @@ abstract class ClassSpecializer<T,K,S extends ClassSpecializer<T,K,S>.SpeciesDat
|
|||
|
||||
// load class
|
||||
InvokerBytecodeGenerator.maybeDump(classBCName(className), classFile);
|
||||
Class<?> speciesCode;
|
||||
|
||||
MethodHandles.Lookup lookup = IMPL_LOOKUP.in(topClass());
|
||||
speciesCode = AccessController.doPrivileged(new PrivilegedAction<>() {
|
||||
@Override
|
||||
public Class<?> run() {
|
||||
try {
|
||||
return lookup.defineClass(classFile);
|
||||
} catch (Exception ex) {
|
||||
throw newInternalError(ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
ClassLoader cl = topClass.getClassLoader();
|
||||
ProtectionDomain pd = null;
|
||||
if (cl != null) {
|
||||
pd = AccessController.doPrivileged(
|
||||
new PrivilegedAction<>() {
|
||||
@Override
|
||||
public ProtectionDomain run() {
|
||||
return topClass().getProtectionDomain();
|
||||
}
|
||||
});
|
||||
}
|
||||
Class<?> speciesCode = SharedSecrets.getJavaLangAccess()
|
||||
.defineClass(cl, className, classFile, pd, "_ClassSpecializer_generateConcreteSpeciesCode");
|
||||
return speciesCode.asSubclass(topClass());
|
||||
}
|
||||
|
||||
|
|
|
@ -31,16 +31,13 @@ import jdk.internal.org.objectweb.asm.ClassWriter;
|
|||
import jdk.internal.org.objectweb.asm.Label;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
import sun.invoke.util.Wrapper;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.function.Function;
|
||||
|
@ -1531,27 +1528,33 @@ public final class StringConcatFactory {
|
|||
if (recipe.getElements().size() == 2) {
|
||||
// Two object arguments
|
||||
if (mt.parameterCount() == 2 &&
|
||||
!mt.parameterType(0).isPrimitive() &&
|
||||
!mt.parameterType(1).isPrimitive()) {
|
||||
return SIMPLE;
|
||||
}
|
||||
// One element is a constant
|
||||
if (mt.parameterCount() == 1 && !mt.parameterType(0).isPrimitive()) {
|
||||
MethodHandle mh = SIMPLE;
|
||||
// Insert constant element
|
||||
!mt.parameterType(0).isPrimitive() &&
|
||||
!mt.parameterType(1).isPrimitive() &&
|
||||
recipe.getElements().get(0).getTag() == TAG_ARG &&
|
||||
recipe.getElements().get(1).getTag() == TAG_ARG) {
|
||||
|
||||
return SIMPLE;
|
||||
|
||||
} else if (mt.parameterCount() == 1 &&
|
||||
!mt.parameterType(0).isPrimitive()) {
|
||||
// One Object argument, one constant
|
||||
MethodHandle mh = SIMPLE;
|
||||
|
||||
// First recipe element is a constant
|
||||
if (recipe.getElements().get(0).getTag() == TAG_CONST &&
|
||||
recipe.getElements().get(1).getTag() != TAG_CONST) {
|
||||
recipe.getElements().get(1).getTag() == TAG_ARG) {
|
||||
// First recipe element is a constant
|
||||
return MethodHandles.insertArguments(mh, 0,
|
||||
recipe.getElements().get(0).getValue());
|
||||
|
||||
} else if (recipe.getElements().get(1).getTag() == TAG_CONST &&
|
||||
recipe.getElements().get(0).getTag() != TAG_CONST) {
|
||||
recipe.getElements().get(0).getTag() == TAG_ARG) {
|
||||
// Second recipe element is a constant
|
||||
return MethodHandles.insertArguments(mh, 1,
|
||||
recipe.getElements().get(1).getValue());
|
||||
|
||||
}
|
||||
// else... fall-through to slow-path
|
||||
}
|
||||
// else... fall-through to slow-path
|
||||
}
|
||||
|
||||
// Create filters and obtain filtered parameter types. Filters would be used in the beginning
|
||||
|
@ -1579,26 +1582,49 @@ public final class StringConcatFactory {
|
|||
|
||||
mh = MethodHandles.dropArguments(NEW_STRING, 2, ptypes);
|
||||
|
||||
long initialLengthCoder = INITIAL_CODER;
|
||||
|
||||
// Mix in prependers. This happens when (byte[], long) = (storage, indexCoder) is already
|
||||
// known from the combinators below. We are assembling the string backwards, so the index coded
|
||||
// into indexCoder is the *ending* index.
|
||||
|
||||
// We need one prepender per argument, but also need to fold in constants. We do so by greedily
|
||||
// create prependers that fold in surrounding constants into the argument prepender. This reduces
|
||||
// the number of unique MH combinator tree shapes we'll create in an application.
|
||||
String prefixConstant = null, suffixConstant = null;
|
||||
int pos = -1;
|
||||
for (RecipeElement el : recipe.getElements()) {
|
||||
// Do the prepend, and put "new" index at index 1
|
||||
switch (el.getTag()) {
|
||||
case TAG_CONST: {
|
||||
MethodHandle prepender = MethodHandles.insertArguments(prepender(String.class), 2, el.getValue());
|
||||
mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, prepender,
|
||||
1, 0 // indexCoder, storage
|
||||
);
|
||||
String constantValue = el.getValue();
|
||||
|
||||
// Eagerly update the initialLengthCoder value
|
||||
initialLengthCoder = (long)mixer(String.class).invoke(initialLengthCoder, constantValue);
|
||||
|
||||
if (pos < 0) {
|
||||
// Collecting into prefixConstant
|
||||
prefixConstant = prefixConstant == null ? constantValue : prefixConstant + constantValue;
|
||||
} else {
|
||||
// Collecting into suffixConstant
|
||||
suffixConstant = suffixConstant == null ? constantValue : suffixConstant + constantValue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TAG_ARG: {
|
||||
int pos = el.getArgPos();
|
||||
MethodHandle prepender = prepender(ptypes[pos]);
|
||||
mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, prepender,
|
||||
|
||||
if (pos >= 0) {
|
||||
// Flush the previous non-constant arg with any prefix/suffix constant
|
||||
mh = MethodHandles.filterArgumentsWithCombiner(
|
||||
mh, 1,
|
||||
prepender(prefixConstant, ptypes[pos], suffixConstant),
|
||||
1, 0, // indexCoder, storage
|
||||
2 + pos // selected argument
|
||||
);
|
||||
);
|
||||
prefixConstant = suffixConstant = null;
|
||||
}
|
||||
// Mark the pos of next non-constant arg
|
||||
pos = el.getArgPos();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -1606,6 +1632,24 @@ public final class StringConcatFactory {
|
|||
}
|
||||
}
|
||||
|
||||
// Insert any trailing args, constants
|
||||
if (pos >= 0) {
|
||||
mh = MethodHandles.filterArgumentsWithCombiner(
|
||||
mh, 1,
|
||||
prepender(prefixConstant, ptypes[pos], suffixConstant),
|
||||
1, 0, // indexCoder, storage
|
||||
2 + pos // selected argument
|
||||
);
|
||||
} else if (prefixConstant != null) {
|
||||
assert (suffixConstant == null);
|
||||
// Sole prefixConstant can only happen if there were no non-constant arguments
|
||||
mh = MethodHandles.filterArgumentsWithCombiner(
|
||||
mh, 1,
|
||||
MethodHandles.insertArguments(prepender(null, String.class, null), 2, prefixConstant),
|
||||
1, 0 // indexCoder, storage
|
||||
);
|
||||
}
|
||||
|
||||
// Fold in byte[] instantiation at argument 0
|
||||
mh = MethodHandles.foldArgumentsWithCombiner(mh, 0, NEW_ARRAY,
|
||||
1 // index
|
||||
|
@ -1624,12 +1668,11 @@ public final class StringConcatFactory {
|
|||
//
|
||||
// The method handle shape before and after all mixers are combined in is:
|
||||
// (long, <args>)String = ("indexCoder", <args>)
|
||||
long initialLengthCoder = INITIAL_CODER;
|
||||
|
||||
for (RecipeElement el : recipe.getElements()) {
|
||||
switch (el.getTag()) {
|
||||
case TAG_CONST:
|
||||
String constant = el.getValue();
|
||||
initialLengthCoder = (long)mixer(String.class).invoke(initialLengthCoder, constant);
|
||||
// Constants already handled in the code above
|
||||
break;
|
||||
case TAG_ARG:
|
||||
int ac = el.getArgPos();
|
||||
|
@ -1661,8 +1704,10 @@ public final class StringConcatFactory {
|
|||
return mh;
|
||||
}
|
||||
|
||||
private static MethodHandle prepender(Class<?> cl) {
|
||||
return PREPENDERS.computeIfAbsent(cl, PREPEND);
|
||||
private static MethodHandle prepender(String prefix, Class<?> cl, String suffix) {
|
||||
return MethodHandles.insertArguments(
|
||||
MethodHandles.insertArguments(
|
||||
PREPENDERS.computeIfAbsent(cl, PREPEND),2, prefix), 3, suffix);
|
||||
}
|
||||
|
||||
private static MethodHandle mixer(Class<?> cl) {
|
||||
|
@ -1670,16 +1715,16 @@ public final class StringConcatFactory {
|
|||
}
|
||||
|
||||
// This one is deliberately non-lambdified to optimize startup time:
|
||||
private static final Function<Class<?>, MethodHandle> PREPEND = new Function<Class<?>, MethodHandle>() {
|
||||
private static final Function<Class<?>, MethodHandle> PREPEND = new Function<>() {
|
||||
@Override
|
||||
public MethodHandle apply(Class<?> c) {
|
||||
return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", long.class, long.class, byte[].class,
|
||||
Wrapper.asPrimitiveType(c));
|
||||
String.class, Wrapper.asPrimitiveType(c), String.class);
|
||||
}
|
||||
};
|
||||
|
||||
// This one is deliberately non-lambdified to optimize startup time:
|
||||
private static final Function<Class<?>, MethodHandle> MIX = new Function<Class<?>, MethodHandle>() {
|
||||
private static final Function<Class<?>, MethodHandle> MIX = new Function<>() {
|
||||
@Override
|
||||
public MethodHandle apply(Class<?> c) {
|
||||
return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mix", long.class, long.class,
|
||||
|
|
|
@ -74,6 +74,12 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
|
|||
/* indicates connection reset state */
|
||||
private volatile boolean connectionReset;
|
||||
|
||||
/* indicates whether impl is bound */
|
||||
boolean isBound;
|
||||
|
||||
/* indicates whether impl is connected */
|
||||
volatile boolean isConnected;
|
||||
|
||||
/* whether this Socket is a stream (TCP) socket or not (UDP)
|
||||
*/
|
||||
protected boolean stream;
|
||||
|
@ -105,6 +111,10 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
|
|||
return isReusePortAvailable;
|
||||
}
|
||||
|
||||
AbstractPlainSocketImpl(boolean isServer) {
|
||||
super(isServer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set of SocketOptions supported by this impl and by this impl's
|
||||
* socket (Socket or ServerSocket)
|
||||
|
@ -148,10 +158,6 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
|
|||
socketCreate(true);
|
||||
SocketCleanable.register(fd);
|
||||
}
|
||||
if (socket != null)
|
||||
socket.setCreated();
|
||||
if (serverSocket != null)
|
||||
serverSocket.setCreated();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -180,6 +186,7 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
|
|||
it will be passed up the call stack */
|
||||
}
|
||||
}
|
||||
isConnected = connected;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -195,6 +202,7 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
|
|||
|
||||
try {
|
||||
connectToAddress(address, port, timeout);
|
||||
isConnected = true;
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
// everything failed
|
||||
|
@ -236,6 +244,7 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
|
|||
it will be passed up the call stack */
|
||||
}
|
||||
}
|
||||
isConnected = connected;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -393,7 +402,7 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
|
|||
|
||||
synchronized void doConnect(InetAddress address, int port, int timeout) throws IOException {
|
||||
synchronized (fdLock) {
|
||||
if (!closePending && (socket == null || !socket.isBound())) {
|
||||
if (!closePending && !isBound) {
|
||||
NetHooks.beforeTcpConnect(fd, address, port);
|
||||
}
|
||||
}
|
||||
|
@ -407,14 +416,6 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
|
|||
throw new SocketException ("Socket closed");
|
||||
}
|
||||
}
|
||||
// If we have a ref. to the Socket, then sets the flags
|
||||
// created, bound & connected to true.
|
||||
// This is normally done in Socket.connect() but some
|
||||
// subclasses of Socket may call impl.connect() directly!
|
||||
if (socket != null) {
|
||||
socket.setBound();
|
||||
socket.setConnected();
|
||||
}
|
||||
} finally {
|
||||
releaseFD();
|
||||
}
|
||||
|
@ -433,15 +434,12 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
|
|||
throws IOException
|
||||
{
|
||||
synchronized (fdLock) {
|
||||
if (!closePending && (socket == null || !socket.isBound())) {
|
||||
if (!closePending && !isBound) {
|
||||
NetHooks.beforeTcpBind(fd, address, lport);
|
||||
}
|
||||
}
|
||||
socketBind(address, lport);
|
||||
if (socket != null)
|
||||
socket.setBound();
|
||||
if (serverSocket != null)
|
||||
serverSocket.setBound();
|
||||
isBound = true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -727,7 +725,7 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
|
|||
socketClose0(false);
|
||||
}
|
||||
|
||||
abstract void socketCreate(boolean isServer) throws IOException;
|
||||
abstract void socketCreate(boolean stream) throws IOException;
|
||||
abstract void socketConnect(InetAddress address, int port, int timeout)
|
||||
throws IOException;
|
||||
abstract void socketBind(InetAddress address, int port)
|
||||
|
|
|
@ -51,6 +51,7 @@ import java.util.Set;
|
|||
private static final Method doTunneling;
|
||||
|
||||
private final String server;
|
||||
private final Socket socket;
|
||||
private InetSocketAddress external_address;
|
||||
private HashMap<Integer, Object> optionsMap = new HashMap<>();
|
||||
|
||||
|
@ -75,8 +76,9 @@ import java.util.Set;
|
|||
}
|
||||
}
|
||||
|
||||
HttpConnectSocketImpl(Proxy proxy, SocketImpl delegate) {
|
||||
HttpConnectSocketImpl(Proxy proxy, SocketImpl delegate, Socket socket) {
|
||||
super(delegate);
|
||||
this.socket = socket;
|
||||
SocketAddress a = proxy.address();
|
||||
if ( !(a instanceof InetSocketAddress) )
|
||||
throw new IllegalArgumentException("Unsupported address type");
|
||||
|
@ -96,17 +98,6 @@ import java.util.Set;
|
|||
connect(new InetSocketAddress(address, port), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
void setSocket(Socket socket) {
|
||||
delegate.socket = socket;
|
||||
super.setSocket(socket);
|
||||
}
|
||||
|
||||
@Override
|
||||
void setServerSocket(ServerSocket socket) {
|
||||
throw new InternalError("should not get here");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void connect(SocketAddress endpoint, int timeout)
|
||||
throws IOException
|
||||
|
@ -137,7 +128,7 @@ import java.util.Set;
|
|||
|
||||
// update the Sockets impl to the impl from the http Socket
|
||||
SocketImpl si = httpSocket.impl;
|
||||
getSocket().setImpl(si);
|
||||
socket.setImpl(si);
|
||||
|
||||
// best effort is made to try and reset options previously set
|
||||
Set<Map.Entry<Integer,Object>> options = optionsMap.entrySet();
|
||||
|
|
|
@ -71,11 +71,6 @@ class ServerSocket implements java.io.Closeable {
|
|||
*/
|
||||
private SocketImpl impl;
|
||||
|
||||
/**
|
||||
* Are we using an older SocketImpl?
|
||||
*/
|
||||
private boolean oldImpl = false;
|
||||
|
||||
/**
|
||||
* Creates a server socket with a user-specified {@code SocketImpl}.
|
||||
*
|
||||
|
@ -87,7 +82,6 @@ class ServerSocket implements java.io.Closeable {
|
|||
*/
|
||||
protected ServerSocket(SocketImpl impl) {
|
||||
this.impl = impl;
|
||||
impl.setServerSocket(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -270,36 +264,13 @@ class ServerSocket implements java.io.Closeable {
|
|||
return impl;
|
||||
}
|
||||
|
||||
private void checkOldImpl() {
|
||||
if (impl == null)
|
||||
return;
|
||||
// SocketImpl.connect() is a protected method, therefore we need to use
|
||||
// getDeclaredMethod, therefore we need permission to access the member
|
||||
try {
|
||||
AccessController.doPrivileged(
|
||||
new PrivilegedExceptionAction<Void>() {
|
||||
public Void run() throws NoSuchMethodException {
|
||||
impl.getClass().getDeclaredMethod("connect",
|
||||
SocketAddress.class,
|
||||
int.class);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
} catch (java.security.PrivilegedActionException e) {
|
||||
oldImpl = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void setImpl() {
|
||||
SocketImplFactory factory = ServerSocket.factory;
|
||||
if (factory != null) {
|
||||
impl = factory.createSocketImpl();
|
||||
checkOldImpl();
|
||||
} else {
|
||||
impl = SocketImpl.createPlatformSocketImpl(true);
|
||||
}
|
||||
if (impl != null)
|
||||
impl.setServerSocket(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -368,7 +339,7 @@ class ServerSocket implements java.io.Closeable {
|
|||
public void bind(SocketAddress endpoint, int backlog) throws IOException {
|
||||
if (isClosed())
|
||||
throw new SocketException("Socket is closed");
|
||||
if (!oldImpl && isBound())
|
||||
if (isBound())
|
||||
throw new SocketException("Already bound");
|
||||
if (endpoint == null)
|
||||
endpoint = new InetSocketAddress(0);
|
||||
|
@ -722,8 +693,7 @@ class ServerSocket implements java.io.Closeable {
|
|||
* @since 1.4
|
||||
*/
|
||||
public boolean isBound() {
|
||||
// Before 1.3 ServerSockets were always bound during creation
|
||||
return bound || oldImpl;
|
||||
return bound;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -866,14 +836,6 @@ class ServerSocket implements java.io.Closeable {
|
|||
",localport=" + impl.getLocalPort() + "]";
|
||||
}
|
||||
|
||||
void setBound() {
|
||||
bound = true;
|
||||
}
|
||||
|
||||
void setCreated() {
|
||||
created = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The factory for all server sockets.
|
||||
*/
|
||||
|
|
|
@ -71,11 +71,6 @@ class Socket implements java.io.Closeable {
|
|||
*/
|
||||
SocketImpl impl;
|
||||
|
||||
/**
|
||||
* Are we using an older SocketImpl?
|
||||
*/
|
||||
private boolean oldImpl = false;
|
||||
|
||||
/**
|
||||
* Socket input/output streams
|
||||
*/
|
||||
|
@ -158,8 +153,7 @@ class Socket implements java.io.Closeable {
|
|||
// create a SOCKS or HTTP SocketImpl that delegates to a platform SocketImpl
|
||||
SocketImpl delegate = SocketImpl.createPlatformSocketImpl(false);
|
||||
impl = (type == Proxy.Type.SOCKS) ? new SocksSocketImpl(p, delegate)
|
||||
: new HttpConnectSocketImpl(p, delegate);
|
||||
impl.setSocket(this);
|
||||
: new HttpConnectSocketImpl(p, delegate, this);
|
||||
} else {
|
||||
if (p == Proxy.NO_PROXY) {
|
||||
// create a platform or custom SocketImpl for the DIRECT case
|
||||
|
@ -169,7 +163,6 @@ class Socket implements java.io.Closeable {
|
|||
} else {
|
||||
impl = factory.createSocketImpl();
|
||||
}
|
||||
impl.setSocket(this);
|
||||
} else
|
||||
throw new IllegalArgumentException("Invalid Proxy");
|
||||
}
|
||||
|
@ -188,10 +181,6 @@ class Socket implements java.io.Closeable {
|
|||
*/
|
||||
protected Socket(SocketImpl impl) throws SocketException {
|
||||
this.impl = impl;
|
||||
if (impl != null) {
|
||||
checkOldImpl();
|
||||
this.impl.setSocket(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -486,37 +475,8 @@ class Socket implements java.io.Closeable {
|
|||
}
|
||||
}
|
||||
|
||||
private void checkOldImpl() {
|
||||
if (impl == null)
|
||||
return;
|
||||
// SocketImpl.connect() is a protected method, therefore we need to use
|
||||
// getDeclaredMethod, therefore we need permission to access the member
|
||||
|
||||
oldImpl = AccessController.doPrivileged
|
||||
(new PrivilegedAction<>() {
|
||||
public Boolean run() {
|
||||
Class<?> clazz = impl.getClass();
|
||||
while (true) {
|
||||
try {
|
||||
clazz.getDeclaredMethod("connect", SocketAddress.class, int.class);
|
||||
return Boolean.FALSE;
|
||||
} catch (NoSuchMethodException e) {
|
||||
clazz = clazz.getSuperclass();
|
||||
// java.net.SocketImpl class will always have this abstract method.
|
||||
// If we have not found it by now in the hierarchy then it does not
|
||||
// exist, we are an old style impl.
|
||||
if (clazz.equals(java.net.SocketImpl.class)) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void setImpl(SocketImpl si) {
|
||||
impl = si;
|
||||
impl.setSocket(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -527,14 +487,11 @@ class Socket implements java.io.Closeable {
|
|||
SocketImplFactory factory = Socket.factory;
|
||||
if (factory != null) {
|
||||
impl = factory.createSocketImpl();
|
||||
checkOldImpl();
|
||||
} else {
|
||||
// create a SOCKS SocketImpl that delegates to a platform SocketImpl
|
||||
SocketImpl delegate = SocketImpl.createPlatformSocketImpl(false);
|
||||
impl = new SocksSocketImpl(delegate);
|
||||
}
|
||||
if (impl != null)
|
||||
impl.setSocket(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -596,7 +553,7 @@ class Socket implements java.io.Closeable {
|
|||
if (isClosed())
|
||||
throw new SocketException("Socket is closed");
|
||||
|
||||
if (!oldImpl && isConnected())
|
||||
if (isConnected())
|
||||
throw new SocketException("already connected");
|
||||
|
||||
if (!(endpoint instanceof InetSocketAddress))
|
||||
|
@ -616,15 +573,7 @@ class Socket implements java.io.Closeable {
|
|||
}
|
||||
if (!created)
|
||||
createImpl(true);
|
||||
if (!oldImpl)
|
||||
impl.connect(epoint, timeout);
|
||||
else if (timeout == 0) {
|
||||
if (epoint.isUnresolved())
|
||||
impl.connect(addr.getHostName(), port);
|
||||
else
|
||||
impl.connect(addr, port);
|
||||
} else
|
||||
throw new UnsupportedOperationException("SocketImpl.connect(addr, timeout)");
|
||||
impl.connect(epoint, timeout);
|
||||
connected = true;
|
||||
/*
|
||||
* If the socket was not bound before the connect, it is now because
|
||||
|
@ -654,7 +603,7 @@ class Socket implements java.io.Closeable {
|
|||
public void bind(SocketAddress bindpoint) throws IOException {
|
||||
if (isClosed())
|
||||
throw new SocketException("Socket is closed");
|
||||
if (!oldImpl && isBound())
|
||||
if (isBound())
|
||||
throw new SocketException("Already bound");
|
||||
|
||||
if (bindpoint != null && (!(bindpoint instanceof InetSocketAddress)))
|
||||
|
@ -694,18 +643,6 @@ class Socket implements java.io.Closeable {
|
|||
bound = true;
|
||||
}
|
||||
|
||||
void setCreated() {
|
||||
created = true;
|
||||
}
|
||||
|
||||
void setBound() {
|
||||
bound = true;
|
||||
}
|
||||
|
||||
void setConnected() {
|
||||
connected = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address to which the socket is connected.
|
||||
* <p>
|
||||
|
@ -957,6 +894,7 @@ class Socket implements java.io.Closeable {
|
|||
private static class SocketInputStream extends InputStream {
|
||||
private final Socket parent;
|
||||
private final InputStream in;
|
||||
|
||||
SocketInputStream(Socket parent, InputStream in) {
|
||||
this.parent = parent;
|
||||
this.in = in;
|
||||
|
@ -975,6 +913,7 @@ class Socket implements java.io.Closeable {
|
|||
public int available() throws IOException {
|
||||
return in.available();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
parent.close();
|
||||
|
@ -1040,6 +979,7 @@ class Socket implements java.io.Closeable {
|
|||
public void write(byte b[], int off, int len) throws IOException {
|
||||
out.write(b, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
parent.close();
|
||||
|
@ -1672,8 +1612,7 @@ class Socket implements java.io.Closeable {
|
|||
* @since 1.4
|
||||
*/
|
||||
public boolean isConnected() {
|
||||
// Before 1.3 Sockets were always connected during creation
|
||||
return connected || oldImpl;
|
||||
return connected;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1689,8 +1628,7 @@ class Socket implements java.io.Closeable {
|
|||
* @see #bind
|
||||
*/
|
||||
public boolean isBound() {
|
||||
// Before 1.3 Sockets were always bound during creation
|
||||
return bound || oldImpl;
|
||||
return bound;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -51,15 +51,9 @@ public abstract class SocketImpl implements SocketOptions {
|
|||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static <S extends SocketImpl & PlatformSocketImpl> S createPlatformSocketImpl(boolean server) {
|
||||
return (S) new PlainSocketImpl();
|
||||
return (S) new PlainSocketImpl(server);
|
||||
}
|
||||
|
||||
/**
|
||||
* The actual Socket object.
|
||||
*/
|
||||
Socket socket = null;
|
||||
ServerSocket serverSocket = null;
|
||||
|
||||
/**
|
||||
* The file descriptor object for this socket.
|
||||
*/
|
||||
|
@ -80,6 +74,23 @@ public abstract class SocketImpl implements SocketOptions {
|
|||
*/
|
||||
protected int localport;
|
||||
|
||||
/**
|
||||
* Whether this is a server or not.
|
||||
*/
|
||||
final boolean isServer;
|
||||
|
||||
|
||||
SocketImpl(boolean isServer) {
|
||||
this.isServer = isServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a new instance of this class
|
||||
*/
|
||||
public SocketImpl() {
|
||||
this.isServer = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates either a stream or a datagram socket.
|
||||
*
|
||||
|
@ -300,22 +311,6 @@ public abstract class SocketImpl implements SocketOptions {
|
|||
return localport;
|
||||
}
|
||||
|
||||
void setSocket(Socket soc) {
|
||||
this.socket = soc;
|
||||
}
|
||||
|
||||
Socket getSocket() {
|
||||
return socket;
|
||||
}
|
||||
|
||||
void setServerSocket(ServerSocket soc) {
|
||||
this.serverSocket = soc;
|
||||
}
|
||||
|
||||
ServerSocket getServerSocket() {
|
||||
return serverSocket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address and port of this socket as a {@code String}.
|
||||
*
|
||||
|
@ -395,11 +390,9 @@ public abstract class SocketImpl implements SocketOptions {
|
|||
* @since 9
|
||||
*/
|
||||
protected <T> void setOption(SocketOption<T> name, T value) throws IOException {
|
||||
if (name == StandardSocketOptions.SO_KEEPALIVE &&
|
||||
(getSocket() != null)) {
|
||||
if (name == StandardSocketOptions.SO_KEEPALIVE && !isServer) {
|
||||
setOption(SocketOptions.SO_KEEPALIVE, value);
|
||||
} else if (name == StandardSocketOptions.SO_SNDBUF &&
|
||||
(getSocket() != null)) {
|
||||
} else if (name == StandardSocketOptions.SO_SNDBUF && !isServer) {
|
||||
setOption(SocketOptions.SO_SNDBUF, value);
|
||||
} else if (name == StandardSocketOptions.SO_RCVBUF) {
|
||||
setOption(SocketOptions.SO_RCVBUF, value);
|
||||
|
@ -408,13 +401,11 @@ public abstract class SocketImpl implements SocketOptions {
|
|||
} else if (name == StandardSocketOptions.SO_REUSEPORT &&
|
||||
supportedOptions().contains(name)) {
|
||||
setOption(SocketOptions.SO_REUSEPORT, value);
|
||||
} else if (name == StandardSocketOptions.SO_LINGER &&
|
||||
(getSocket() != null)) {
|
||||
} else if (name == StandardSocketOptions.SO_LINGER && !isServer) {
|
||||
setOption(SocketOptions.SO_LINGER, value);
|
||||
} else if (name == StandardSocketOptions.IP_TOS) {
|
||||
setOption(SocketOptions.IP_TOS, value);
|
||||
} else if (name == StandardSocketOptions.TCP_NODELAY &&
|
||||
(getSocket() != null)) {
|
||||
} else if (name == StandardSocketOptions.TCP_NODELAY && !isServer) {
|
||||
setOption(SocketOptions.TCP_NODELAY, value);
|
||||
} else {
|
||||
throw new UnsupportedOperationException("unsupported option");
|
||||
|
@ -438,11 +429,9 @@ public abstract class SocketImpl implements SocketOptions {
|
|||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T> T getOption(SocketOption<T> name) throws IOException {
|
||||
if (name == StandardSocketOptions.SO_KEEPALIVE &&
|
||||
(getSocket() != null)) {
|
||||
if (name == StandardSocketOptions.SO_KEEPALIVE && !isServer) {
|
||||
return (T)getOption(SocketOptions.SO_KEEPALIVE);
|
||||
} else if (name == StandardSocketOptions.SO_SNDBUF &&
|
||||
(getSocket() != null)) {
|
||||
} else if (name == StandardSocketOptions.SO_SNDBUF && !isServer) {
|
||||
return (T)getOption(SocketOptions.SO_SNDBUF);
|
||||
} else if (name == StandardSocketOptions.SO_RCVBUF) {
|
||||
return (T)getOption(SocketOptions.SO_RCVBUF);
|
||||
|
@ -451,13 +440,11 @@ public abstract class SocketImpl implements SocketOptions {
|
|||
} else if (name == StandardSocketOptions.SO_REUSEPORT &&
|
||||
supportedOptions().contains(name)) {
|
||||
return (T)getOption(SocketOptions.SO_REUSEPORT);
|
||||
} else if (name == StandardSocketOptions.SO_LINGER &&
|
||||
(getSocket() != null)) {
|
||||
} else if (name == StandardSocketOptions.SO_LINGER && !isServer) {
|
||||
return (T)getOption(SocketOptions.SO_LINGER);
|
||||
} else if (name == StandardSocketOptions.IP_TOS) {
|
||||
return (T)getOption(SocketOptions.IP_TOS);
|
||||
} else if (name == StandardSocketOptions.TCP_NODELAY &&
|
||||
(getSocket() != null)) {
|
||||
} else if (name == StandardSocketOptions.TCP_NODELAY && !isServer) {
|
||||
return (T)getOption(SocketOptions.TCP_NODELAY);
|
||||
} else {
|
||||
throw new UnsupportedOperationException("unsupported option");
|
||||
|
@ -504,7 +491,7 @@ public abstract class SocketImpl implements SocketOptions {
|
|||
* @since 9
|
||||
*/
|
||||
protected Set<SocketOption<?>> supportedOptions() {
|
||||
if (getSocket() != null) {
|
||||
if (!isServer) {
|
||||
return socketOptions;
|
||||
} else {
|
||||
return serverSocketOptions;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1995, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1995, 2019, 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
|
||||
|
@ -48,7 +48,6 @@ class SocketInputStream extends FileInputStream {
|
|||
private boolean eof;
|
||||
private AbstractPlainSocketImpl impl = null;
|
||||
private byte temp[];
|
||||
private Socket socket = null;
|
||||
|
||||
/**
|
||||
* Creates a new SocketInputStream. Can only be called
|
||||
|
@ -59,7 +58,6 @@ class SocketInputStream extends FileInputStream {
|
|||
SocketInputStream(AbstractPlainSocketImpl impl) throws IOException {
|
||||
super(impl.getFileDescriptor());
|
||||
this.impl = impl;
|
||||
socket = impl.getSocket();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -236,27 +234,16 @@ class SocketInputStream extends FileInputStream {
|
|||
return eof ? 0 : available;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the stream.
|
||||
*/
|
||||
private boolean closing = false;
|
||||
public void close() throws IOException {
|
||||
// Prevent recursion. See BugId 4484411
|
||||
if (closing)
|
||||
return;
|
||||
closing = true;
|
||||
if (socket != null) {
|
||||
if (!socket.isClosed())
|
||||
socket.close();
|
||||
} else
|
||||
impl.close();
|
||||
closing = false;
|
||||
}
|
||||
|
||||
void setEOF(boolean eof) {
|
||||
this.eof = eof;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
// No longer used. Socket.getInputStream returns an
|
||||
// InputStream which calls Socket.close directly
|
||||
assert false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides finalize, the fd is closed by the Socket.
|
||||
*/
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1995, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1995, 2019, 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
|
||||
|
@ -45,7 +45,6 @@ class SocketOutputStream extends FileOutputStream {
|
|||
|
||||
private AbstractPlainSocketImpl impl = null;
|
||||
private byte temp[] = new byte[1];
|
||||
private Socket socket = null;
|
||||
|
||||
/**
|
||||
* Creates a new SocketOutputStream. Can only be called
|
||||
|
@ -56,7 +55,6 @@ class SocketOutputStream extends FileOutputStream {
|
|||
SocketOutputStream(AbstractPlainSocketImpl impl) throws IOException {
|
||||
super(impl.getFileDescriptor());
|
||||
this.impl = impl;
|
||||
socket = impl.getSocket();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -150,21 +148,10 @@ class SocketOutputStream extends FileOutputStream {
|
|||
socketWrite(b, off, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the stream.
|
||||
*/
|
||||
private boolean closing = false;
|
||||
public void close() throws IOException {
|
||||
// Prevent recursion. See BugId 4484411
|
||||
if (closing)
|
||||
return;
|
||||
closing = true;
|
||||
if (socket != null) {
|
||||
if (!socket.isClosed())
|
||||
socket.close();
|
||||
} else
|
||||
impl.close();
|
||||
closing = false;
|
||||
// No longer used. Socket.getOutputStream returns an
|
||||
// OutputStream which calls Socket.close directly
|
||||
assert false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -256,17 +256,6 @@ class SocksSocketImpl extends DelegatingSocketImpl implements SocksConsts {
|
|||
connect(new InetSocketAddress(address, port), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
void setSocket(Socket soc) {
|
||||
delegate.socket = soc;
|
||||
super.setSocket(soc);
|
||||
}
|
||||
|
||||
@Override
|
||||
void setServerSocket(ServerSocket soc) {
|
||||
throw new InternalError("should not get here");
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects the Socks Socket to the specified endpoint. It will first
|
||||
* connect to the SOCKS proxy and negotiate the access. If the proxy
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2019, 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,15 @@
|
|||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
package java.nio.channels.spi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.*;
|
||||
import java.nio.channels.AsynchronousCloseException;
|
||||
import java.nio.channels.Channel;
|
||||
import java.nio.channels.ClosedByInterruptException;
|
||||
import java.nio.channels.InterruptibleChannel;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import sun.nio.ch.Interruptible;
|
||||
|
||||
|
@ -84,8 +86,7 @@ import sun.nio.ch.Interruptible;
|
|||
public abstract class AbstractInterruptibleChannel
|
||||
implements Channel, InterruptibleChannel
|
||||
{
|
||||
|
||||
private final Object closeLock = new Object();
|
||||
private final ReentrantLock closeLock = new ReentrantLock();
|
||||
private volatile boolean closed;
|
||||
|
||||
/**
|
||||
|
@ -105,11 +106,14 @@ public abstract class AbstractInterruptibleChannel
|
|||
* If an I/O error occurs
|
||||
*/
|
||||
public final void close() throws IOException {
|
||||
synchronized (closeLock) {
|
||||
closeLock.lock();
|
||||
try {
|
||||
if (closed)
|
||||
return;
|
||||
closed = true;
|
||||
implCloseChannel();
|
||||
} finally {
|
||||
closeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -153,7 +157,8 @@ public abstract class AbstractInterruptibleChannel
|
|||
if (interruptor == null) {
|
||||
interruptor = new Interruptible() {
|
||||
public void interrupt(Thread target) {
|
||||
synchronized (closeLock) {
|
||||
closeLock.lock();
|
||||
try {
|
||||
if (closed)
|
||||
return;
|
||||
closed = true;
|
||||
|
@ -161,6 +166,8 @@ public abstract class AbstractInterruptibleChannel
|
|||
try {
|
||||
AbstractInterruptibleChannel.this.implCloseChannel();
|
||||
} catch (IOException x) { }
|
||||
} finally {
|
||||
closeLock.unlock();
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2019, 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
|
||||
|
@ -74,7 +74,7 @@ package java.security;
|
|||
* </UL>
|
||||
*
|
||||
* Keys are generally obtained through key generators, certificates,
|
||||
* or various Identity classes used to manage keys.
|
||||
* key stores or other classes used to manage keys.
|
||||
* Keys may also be obtained from key specifications (transparent
|
||||
* representations of the underlying key material) through the use of a key
|
||||
* factory (see {@link KeyFactory}).
|
||||
|
@ -119,10 +119,11 @@ public interface Key extends java.io.Serializable {
|
|||
/**
|
||||
* Returns the standard algorithm name for this key. For
|
||||
* example, "DSA" would indicate that this key is a DSA key.
|
||||
* See the <a href=
|
||||
* See the key related sections (KeyFactory, KeyGenerator,
|
||||
* KeyPairGenerator, and SecretKeyFactory) in the <a href=
|
||||
* "{@docRoot}/../specs/security/standard-names.html">
|
||||
* Java Security Standard Algorithm Names</a> document
|
||||
* for more information.
|
||||
* Java Security Standard Algorithm Names Specification</a>
|
||||
* for information about standard key algorithm names.
|
||||
*
|
||||
* @return the name of the algorithm associated with this key.
|
||||
*/
|
||||
|
|
|
@ -1129,6 +1129,7 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
|
|||
clone.entrySet = null;
|
||||
clone.values = null;
|
||||
clone.descendingMap = null;
|
||||
clone.adder = null;
|
||||
clone.buildFromSorted(this);
|
||||
return clone;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
|
|
|
@ -413,8 +413,9 @@ public class CopyOnWriteArrayList<E>
|
|||
if (oldValue != element) {
|
||||
es = es.clone();
|
||||
es[index] = element;
|
||||
setArray(es);
|
||||
}
|
||||
// Ensure volatile write semantics even when oldvalue == element
|
||||
setArray(es);
|
||||
return oldValue;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ import java.util.List;
|
|||
* <p>The {@link Executors} class provides factory methods for the
|
||||
* executor services provided in this package.
|
||||
*
|
||||
* <h3>Usage Examples</h3>
|
||||
* <h2>Usage Examples</h2>
|
||||
*
|
||||
* Here is a sketch of a network service in which threads in a thread
|
||||
* pool service incoming requests. It uses the preconfigured {@link
|
||||
|
|
|
@ -65,7 +65,7 @@ package java.util.concurrent;
|
|||
* <p>The {@link Executors} class provides convenient factory methods for
|
||||
* the ScheduledExecutorService implementations provided in this package.
|
||||
*
|
||||
* <h3>Usage Example</h3>
|
||||
* <h2>Usage Example</h2>
|
||||
*
|
||||
* Here is a class with a method that sets up a ScheduledExecutorService
|
||||
* to beep every ten seconds for an hour:
|
||||
|
|
|
@ -922,13 +922,13 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
|
|||
|
||||
if (isRunning(c) ||
|
||||
(runStateLessThan(c, STOP) && firstTask == null)) {
|
||||
if (t.isAlive()) // precheck that t is startable
|
||||
if (t.getState() != Thread.State.NEW)
|
||||
throw new IllegalThreadStateException();
|
||||
workers.add(w);
|
||||
workerAdded = true;
|
||||
int s = workers.size();
|
||||
if (s > largestPoolSize)
|
||||
largestPoolSize = s;
|
||||
workerAdded = true;
|
||||
}
|
||||
} finally {
|
||||
mainLock.unlock();
|
||||
|
|
|
@ -104,7 +104,7 @@ import java.util.concurrent.TimeUnit;
|
|||
* define a {@code readObject} method that restores this to a known
|
||||
* initial state upon deserialization.
|
||||
*
|
||||
* <h3>Usage</h3>
|
||||
* <h2>Usage</h2>
|
||||
*
|
||||
* <p>To use this class as the basis of a synchronizer, redefine the
|
||||
* following methods, as applicable, by inspecting and/or modifying
|
||||
|
@ -187,7 +187,7 @@ import java.util.concurrent.TimeUnit;
|
|||
* {@link java.util.Queue} classes, and {@link LockSupport} blocking
|
||||
* support.
|
||||
*
|
||||
* <h3>Usage Examples</h3>
|
||||
* <h2>Usage Examples</h2>
|
||||
*
|
||||
* <p>Here is a non-reentrant mutual exclusion lock class that uses
|
||||
* the value zero to represent the unlocked state, and one to
|
||||
|
|
|
@ -138,7 +138,7 @@ import java.util.concurrent.TimeUnit;
|
|||
* <p>Except where noted, passing a {@code null} value for any parameter
|
||||
* will result in a {@link NullPointerException} being thrown.
|
||||
*
|
||||
* <h3>Implementation Considerations</h3>
|
||||
* <h2>Implementation Considerations</h2>
|
||||
*
|
||||
* <p>When waiting upon a {@code Condition}, a "<em>spurious
|
||||
* wakeup</em>" is permitted to occur, in
|
||||
|
|
|
@ -117,7 +117,7 @@ import java.util.concurrent.TimeUnit;
|
|||
* parameter will result in a {@link NullPointerException} being
|
||||
* thrown.
|
||||
*
|
||||
* <h3>Memory Synchronization</h3>
|
||||
* <h2>Memory Synchronization</h2>
|
||||
*
|
||||
* <p>All {@code Lock} implementations <em>must</em> enforce the same
|
||||
* memory synchronization semantics as provided by the built-in monitor
|
||||
|
@ -136,7 +136,7 @@ import java.util.concurrent.TimeUnit;
|
|||
* locking/unlocking operations, do not require any memory
|
||||
* synchronization effects.
|
||||
*
|
||||
* <h3>Implementation Considerations</h3>
|
||||
* <h2>Implementation Considerations</h2>
|
||||
*
|
||||
* <p>The three forms of lock acquisition (interruptible,
|
||||
* non-interruptible, and timed) may differ in their performance
|
||||
|
|
|
@ -204,7 +204,7 @@ import jdk.internal.vm.annotation.ReservedStackAccess;
|
|||
* }
|
||||
* }}</pre>
|
||||
*
|
||||
* <h3>Implementation Notes</h3>
|
||||
* <h2>Implementation Notes</h2>
|
||||
*
|
||||
* <p>This lock supports a maximum of 65535 recursive write locks
|
||||
* and 65535 read locks. Attempts to exceed these limits result in
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2019, 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
|
||||
|
@ -214,6 +214,7 @@ module java.base {
|
|||
java.sql,
|
||||
java.sql.rowset,
|
||||
jdk.dynalink,
|
||||
jdk.internal.vm.ci,
|
||||
jdk.scripting.nashorn,
|
||||
jdk.unsupported;
|
||||
exports jdk.internal.vm to
|
||||
|
|
|
@ -146,7 +146,7 @@ public final class ParseUtil {
|
|||
|
||||
/**
|
||||
* Appends the URL escape sequence for the specified char to the
|
||||
* specified StringBuffer.
|
||||
* specified character array.
|
||||
*/
|
||||
private static int escape(char[] cc, char c, int index) {
|
||||
cc[index++] = '%';
|
||||
|
@ -336,7 +336,7 @@ public final class ParseUtil {
|
|||
String query,
|
||||
String fragment)
|
||||
{
|
||||
StringBuffer sb = new StringBuffer();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (scheme != null) {
|
||||
sb.append(scheme);
|
||||
sb.append(':');
|
||||
|
@ -348,7 +348,7 @@ public final class ParseUtil {
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
private static void appendSchemeSpecificPart(StringBuffer sb,
|
||||
private static void appendSchemeSpecificPart(StringBuilder sb,
|
||||
String opaquePart,
|
||||
String authority,
|
||||
String userInfo,
|
||||
|
@ -389,7 +389,7 @@ public final class ParseUtil {
|
|||
}
|
||||
}
|
||||
|
||||
private static void appendAuthority(StringBuffer sb,
|
||||
private static void appendAuthority(StringBuilder sb,
|
||||
String authority,
|
||||
String userInfo,
|
||||
String host,
|
||||
|
@ -437,7 +437,7 @@ public final class ParseUtil {
|
|||
}
|
||||
}
|
||||
|
||||
private static void appendFragment(StringBuffer sb, String fragment) {
|
||||
private static void appendFragment(StringBuilder sb, String fragment) {
|
||||
if (fragment != null) {
|
||||
sb.append('#');
|
||||
sb.append(quote(fragment, L_URIC, H_URIC));
|
||||
|
@ -449,14 +449,14 @@ public final class ParseUtil {
|
|||
//
|
||||
private static String quote(String s, long lowMask, long highMask) {
|
||||
int n = s.length();
|
||||
StringBuffer sb = null;
|
||||
StringBuilder sb = null;
|
||||
boolean allowNonASCII = ((lowMask & L_ESCAPED) != 0);
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char c = s.charAt(i);
|
||||
if (c < '\u0080') {
|
||||
if (!match(c, lowMask, highMask) && !isEscaped(s, i)) {
|
||||
if (sb == null) {
|
||||
sb = new StringBuffer();
|
||||
sb = new StringBuilder();
|
||||
sb.append(s, 0, i);
|
||||
}
|
||||
appendEscape(sb, (byte)c);
|
||||
|
@ -468,7 +468,7 @@ public final class ParseUtil {
|
|||
&& (Character.isSpaceChar(c)
|
||||
|| Character.isISOControl(c))) {
|
||||
if (sb == null) {
|
||||
sb = new StringBuffer();
|
||||
sb = new StringBuilder();
|
||||
sb.append(s, 0, i);
|
||||
}
|
||||
appendEncoded(sb, c);
|
||||
|
@ -493,7 +493,7 @@ public final class ParseUtil {
|
|||
&& match(s.charAt(pos + 2), L_HEX, H_HEX);
|
||||
}
|
||||
|
||||
private static void appendEncoded(StringBuffer sb, char c) {
|
||||
private static void appendEncoded(StringBuilder sb, char c) {
|
||||
ByteBuffer bb = null;
|
||||
try {
|
||||
bb = ThreadLocalCoders.encoderFor("UTF-8")
|
||||
|
@ -515,7 +515,7 @@ public final class ParseUtil {
|
|||
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
|
||||
};
|
||||
|
||||
private static void appendEscape(StringBuffer sb, byte b) {
|
||||
private static void appendEscape(StringBuilder sb, byte b) {
|
||||
sb.append('%');
|
||||
sb.append(hexDigits[(b >> 4) & 0x0f]);
|
||||
sb.append(hexDigits[(b >> 0) & 0x0f]);
|
||||
|
|
|
@ -53,6 +53,7 @@ import java.util.Collections;
|
|||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import sun.net.ResourceManager;
|
||||
|
@ -89,7 +90,8 @@ class DatagramChannelImpl
|
|||
|
||||
// Lock held by any thread that modifies the state fields declared below
|
||||
// DO NOT invoke a blocking I/O operation while holding this lock!
|
||||
private final Object stateLock = new Object();
|
||||
private final ReentrantLock stateLock = new ReentrantLock();
|
||||
private final Condition stateCondition = stateLock.newCondition();
|
||||
|
||||
// -- The following fields are protected by stateLock
|
||||
|
||||
|
@ -179,8 +181,11 @@ class DatagramChannelImpl
|
|||
: StandardProtocolFamily.INET;
|
||||
this.fd = fd;
|
||||
this.fdVal = IOUtil.fdVal(fd);
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
this.localAddress = Net.localAddress(fd);
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -192,27 +197,36 @@ class DatagramChannelImpl
|
|||
|
||||
@Override
|
||||
public DatagramSocket socket() {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
if (socket == null)
|
||||
socket = DatagramSocketAdaptor.create(this);
|
||||
return socket;
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getLocalAddress() throws IOException {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
// Perform security check before returning address
|
||||
return Net.getRevealedLocalAddress(localAddress);
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getRemoteAddress() throws IOException {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
return remoteAddress;
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -224,7 +238,8 @@ class DatagramChannelImpl
|
|||
if (!supportedOptions().contains(name))
|
||||
throw new UnsupportedOperationException("'" + name + "' not supported");
|
||||
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
|
||||
if (name == StandardSocketOptions.IP_TOS ||
|
||||
|
@ -264,6 +279,8 @@ class DatagramChannelImpl
|
|||
// remaining options don't need any special handling
|
||||
Net.setSocketOption(fd, Net.UNSPEC, name, value);
|
||||
return this;
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -276,7 +293,8 @@ class DatagramChannelImpl
|
|||
if (!supportedOptions().contains(name))
|
||||
throw new UnsupportedOperationException("'" + name + "' not supported");
|
||||
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
|
||||
if (name == StandardSocketOptions.IP_TOS ||
|
||||
|
@ -315,6 +333,8 @@ class DatagramChannelImpl
|
|||
|
||||
// no special handling
|
||||
return (T) Net.getSocketOption(fd, Net.UNSPEC, name);
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -362,7 +382,8 @@ class DatagramChannelImpl
|
|||
begin();
|
||||
}
|
||||
SocketAddress remote;
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
remote = remoteAddress;
|
||||
if ((remote == null) && mustBeConnected)
|
||||
|
@ -371,6 +392,8 @@ class DatagramChannelImpl
|
|||
bindInternal(null);
|
||||
if (blocking)
|
||||
readerThread = NativeThread.current();
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
return remote;
|
||||
}
|
||||
|
@ -384,12 +407,15 @@ class DatagramChannelImpl
|
|||
throws AsynchronousCloseException
|
||||
{
|
||||
if (blocking) {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
readerThread = 0;
|
||||
// notify any thread waiting in implCloseSelectableChannel
|
||||
if (state == ST_CLOSING) {
|
||||
stateLock.notifyAll();
|
||||
stateCondition.signalAll();
|
||||
}
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
// remove hook for Thread.interrupt
|
||||
end(completed);
|
||||
|
@ -414,21 +440,29 @@ class DatagramChannelImpl
|
|||
SecurityManager sm = System.getSecurityManager();
|
||||
if (connected || (sm == null)) {
|
||||
// connected or no security manager
|
||||
do {
|
||||
n = receive(fd, dst, connected);
|
||||
} while ((n == IOStatus.INTERRUPTED) && isOpen());
|
||||
if (n == IOStatus.UNAVAILABLE)
|
||||
n = receive(fd, dst, connected);
|
||||
if (blocking) {
|
||||
while (IOStatus.okayToRetry(n) && isOpen()) {
|
||||
park(Net.POLLIN);
|
||||
n = receive(fd, dst, connected);
|
||||
}
|
||||
} else if (n == IOStatus.UNAVAILABLE) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
// Cannot receive into user's buffer when running with a
|
||||
// security manager and not connected
|
||||
bb = Util.getTemporaryDirectBuffer(dst.remaining());
|
||||
for (;;) {
|
||||
do {
|
||||
n = receive(fd, bb, connected);
|
||||
} while ((n == IOStatus.INTERRUPTED) && isOpen());
|
||||
if (n == IOStatus.UNAVAILABLE)
|
||||
n = receive(fd, bb, connected);
|
||||
if (blocking) {
|
||||
while (IOStatus.okayToRetry(n) && isOpen()) {
|
||||
park(Net.POLLIN);
|
||||
n = receive(fd, bb, connected);
|
||||
}
|
||||
} else if (n == IOStatus.UNAVAILABLE) {
|
||||
return null;
|
||||
}
|
||||
InetSocketAddress isa = (InetSocketAddress)sender;
|
||||
try {
|
||||
sm.checkAccept(isa.getAddress().getHostAddress(),
|
||||
|
@ -493,6 +527,7 @@ class DatagramChannelImpl
|
|||
return n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int send(ByteBuffer src, SocketAddress target)
|
||||
throws IOException
|
||||
{
|
||||
|
@ -510,9 +545,13 @@ class DatagramChannelImpl
|
|||
if (!target.equals(remote)) {
|
||||
throw new AlreadyConnectedException();
|
||||
}
|
||||
do {
|
||||
n = IOUtil.write(fd, src, -1, nd);
|
||||
} while ((n == IOStatus.INTERRUPTED) && isOpen());
|
||||
n = IOUtil.write(fd, src, -1, nd);
|
||||
if (blocking) {
|
||||
while (IOStatus.okayToRetry(n) && isOpen()) {
|
||||
park(Net.POLLOUT);
|
||||
n = IOUtil.write(fd, src, -1, nd);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// not connected
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
|
@ -524,9 +563,13 @@ class DatagramChannelImpl
|
|||
sm.checkConnect(ia.getHostAddress(), isa.getPort());
|
||||
}
|
||||
}
|
||||
do {
|
||||
n = send(fd, src, isa);
|
||||
} while ((n == IOStatus.INTERRUPTED) && isOpen());
|
||||
n = send(fd, src, isa);
|
||||
if (blocking) {
|
||||
while (IOStatus.okayToRetry(n) && isOpen()) {
|
||||
park(Net.POLLOUT);
|
||||
n = send(fd, src, isa);
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
endWrite(blocking, n > 0);
|
||||
|
@ -602,10 +645,13 @@ class DatagramChannelImpl
|
|||
int n = 0;
|
||||
try {
|
||||
beginRead(blocking, true);
|
||||
do {
|
||||
n = IOUtil.read(fd, buf, -1, nd);
|
||||
} while ((n == IOStatus.INTERRUPTED) && isOpen());
|
||||
|
||||
n = IOUtil.read(fd, buf, -1, nd);
|
||||
if (blocking) {
|
||||
while (IOStatus.okayToRetry(n) && isOpen()) {
|
||||
park(Net.POLLIN);
|
||||
n = IOUtil.read(fd, buf, -1, nd);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
endRead(blocking, n > 0);
|
||||
assert IOStatus.check(n);
|
||||
|
@ -628,10 +674,13 @@ class DatagramChannelImpl
|
|||
long n = 0;
|
||||
try {
|
||||
beginRead(blocking, true);
|
||||
do {
|
||||
n = IOUtil.read(fd, dsts, offset, length, nd);
|
||||
} while ((n == IOStatus.INTERRUPTED) && isOpen());
|
||||
|
||||
n = IOUtil.read(fd, dsts, offset, length, nd);
|
||||
if (blocking) {
|
||||
while (IOStatus.okayToRetry(n) && isOpen()) {
|
||||
park(Net.POLLIN);
|
||||
n = IOUtil.read(fd, dsts, offset, length, nd);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
endRead(blocking, n > 0);
|
||||
assert IOStatus.check(n);
|
||||
|
@ -659,7 +708,8 @@ class DatagramChannelImpl
|
|||
begin();
|
||||
}
|
||||
SocketAddress remote;
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
remote = remoteAddress;
|
||||
if ((remote == null) && mustBeConnected)
|
||||
|
@ -668,6 +718,8 @@ class DatagramChannelImpl
|
|||
bindInternal(null);
|
||||
if (blocking)
|
||||
writerThread = NativeThread.current();
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
return remote;
|
||||
}
|
||||
|
@ -681,12 +733,15 @@ class DatagramChannelImpl
|
|||
throws AsynchronousCloseException
|
||||
{
|
||||
if (blocking) {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
writerThread = 0;
|
||||
// notify any thread waiting in implCloseSelectableChannel
|
||||
if (state == ST_CLOSING) {
|
||||
stateLock.notifyAll();
|
||||
stateCondition.signalAll();
|
||||
}
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
// remove hook for Thread.interrupt
|
||||
end(completed);
|
||||
|
@ -703,9 +758,13 @@ class DatagramChannelImpl
|
|||
int n = 0;
|
||||
try {
|
||||
beginWrite(blocking, true);
|
||||
do {
|
||||
n = IOUtil.write(fd, buf, -1, nd);
|
||||
} while ((n == IOStatus.INTERRUPTED) && isOpen());
|
||||
n = IOUtil.write(fd, buf, -1, nd);
|
||||
if (blocking) {
|
||||
while (IOStatus.okayToRetry(n) && isOpen()) {
|
||||
park(Net.POLLOUT);
|
||||
n = IOUtil.write(fd, buf, -1, nd);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
endWrite(blocking, n > 0);
|
||||
assert IOStatus.check(n);
|
||||
|
@ -728,9 +787,13 @@ class DatagramChannelImpl
|
|||
long n = 0;
|
||||
try {
|
||||
beginWrite(blocking, true);
|
||||
do {
|
||||
n = IOUtil.write(fd, srcs, offset, length, nd);
|
||||
} while ((n == IOStatus.INTERRUPTED) && isOpen());
|
||||
n = IOUtil.write(fd, srcs, offset, length, nd);
|
||||
if (blocking) {
|
||||
while (IOStatus.okayToRetry(n) && isOpen()) {
|
||||
park(Net.POLLOUT);
|
||||
n = IOUtil.write(fd, srcs, offset, length, nd);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
endWrite(blocking, n > 0);
|
||||
assert IOStatus.check(n);
|
||||
|
@ -747,9 +810,12 @@ class DatagramChannelImpl
|
|||
try {
|
||||
writeLock.lock();
|
||||
try {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
IOUtil.configureBlocking(fd, block);
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
|
@ -760,14 +826,20 @@ class DatagramChannelImpl
|
|||
}
|
||||
|
||||
InetSocketAddress localAddress() {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
return localAddress;
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
InetSocketAddress remoteAddress() {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
return remoteAddress;
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -777,11 +849,14 @@ class DatagramChannelImpl
|
|||
try {
|
||||
writeLock.lock();
|
||||
try {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
if (localAddress != null)
|
||||
throw new AlreadyBoundException();
|
||||
bindInternal(local);
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
|
@ -793,7 +868,7 @@ class DatagramChannelImpl
|
|||
}
|
||||
|
||||
private void bindInternal(SocketAddress local) throws IOException {
|
||||
assert Thread.holdsLock(stateLock) && (localAddress == null);
|
||||
assert stateLock.isHeldByCurrentThread() && (localAddress == null);
|
||||
|
||||
InetSocketAddress isa;
|
||||
if (local == null) {
|
||||
|
@ -816,8 +891,11 @@ class DatagramChannelImpl
|
|||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
return (state == ST_CONNECTED);
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -839,7 +917,8 @@ class DatagramChannelImpl
|
|||
try {
|
||||
writeLock.lock();
|
||||
try {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
if (state == ST_CONNECTED)
|
||||
throw new AlreadyConnectedException();
|
||||
|
@ -865,7 +944,7 @@ class DatagramChannelImpl
|
|||
}
|
||||
try {
|
||||
ByteBuffer buf = ByteBuffer.allocate(100);
|
||||
while (receive(buf) != null) {
|
||||
while (receive(fd, buf, false) > 0) {
|
||||
buf.clear();
|
||||
}
|
||||
} finally {
|
||||
|
@ -873,6 +952,9 @@ class DatagramChannelImpl
|
|||
IOUtil.configureBlocking(fd, true);
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
|
@ -889,7 +971,8 @@ class DatagramChannelImpl
|
|||
try {
|
||||
writeLock.lock();
|
||||
try {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
if (!isOpen() || (state != ST_CONNECTED))
|
||||
return this;
|
||||
|
||||
|
@ -903,6 +986,8 @@ class DatagramChannelImpl
|
|||
|
||||
// refresh local address
|
||||
localAddress = Net.localAddress(fd);
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
|
@ -950,7 +1035,8 @@ class DatagramChannelImpl
|
|||
if (sm != null)
|
||||
sm.checkMulticast(group);
|
||||
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
|
||||
// check the registry to see if we are already a member of the group
|
||||
|
@ -1005,6 +1091,8 @@ class DatagramChannelImpl
|
|||
|
||||
registry.add(key);
|
||||
return key;
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1030,7 +1118,8 @@ class DatagramChannelImpl
|
|||
void drop(MembershipKeyImpl key) {
|
||||
assert key.channel() == this;
|
||||
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
if (!key.isValid())
|
||||
return;
|
||||
|
||||
|
@ -1051,6 +1140,8 @@ class DatagramChannelImpl
|
|||
|
||||
key.invalidate();
|
||||
registry.remove(key);
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1064,7 +1155,8 @@ class DatagramChannelImpl
|
|||
assert key.channel() == this;
|
||||
assert key.sourceAddress() == null;
|
||||
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
if (!key.isValid())
|
||||
throw new IllegalStateException("key is no longer valid");
|
||||
if (source.isAnyLocalAddress())
|
||||
|
@ -1090,6 +1182,8 @@ class DatagramChannelImpl
|
|||
// ancient kernel
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1100,7 +1194,8 @@ class DatagramChannelImpl
|
|||
assert key.channel() == this;
|
||||
assert key.sourceAddress() == null;
|
||||
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
if (!key.isValid())
|
||||
throw new IllegalStateException("key is no longer valid");
|
||||
|
||||
|
@ -1120,6 +1215,8 @@ class DatagramChannelImpl
|
|||
// should not happen
|
||||
throw new AssertionError(ioe);
|
||||
}
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1144,7 +1241,8 @@ class DatagramChannelImpl
|
|||
boolean interrupted = false;
|
||||
|
||||
// set state to ST_CLOSING and invalid membership keys
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
assert state < ST_CLOSING;
|
||||
blocking = isBlocking();
|
||||
state = ST_CLOSING;
|
||||
|
@ -1152,11 +1250,14 @@ class DatagramChannelImpl
|
|||
// if member of any multicast groups then invalidate the keys
|
||||
if (registry != null)
|
||||
registry.invalidateAll();
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
|
||||
// wait for any outstanding I/O operations to complete
|
||||
if (blocking) {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
assert state == ST_CLOSING;
|
||||
long reader = readerThread;
|
||||
long writer = writerThread;
|
||||
|
@ -1171,12 +1272,14 @@ class DatagramChannelImpl
|
|||
// wait for blocking I/O operations to end
|
||||
while (readerThread != 0 || writerThread != 0) {
|
||||
try {
|
||||
stateLock.wait();
|
||||
stateCondition.await();
|
||||
} catch (InterruptedException e) {
|
||||
interrupted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
} else {
|
||||
// non-blocking mode: wait for read/write to complete
|
||||
|
@ -1190,9 +1293,12 @@ class DatagramChannelImpl
|
|||
}
|
||||
|
||||
// set state to ST_KILLPENDING
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
assert state == ST_CLOSING;
|
||||
state = ST_KILLPENDING;
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
|
||||
// close socket if not registered with Selector
|
||||
|
@ -1206,7 +1312,8 @@ class DatagramChannelImpl
|
|||
|
||||
@Override
|
||||
public void kill() throws IOException {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
if (state == ST_KILLPENDING) {
|
||||
state = ST_KILLED;
|
||||
try {
|
||||
|
@ -1216,6 +1323,8 @@ class DatagramChannelImpl
|
|||
ResourceManager.afterUdpClose();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2019, 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,6 +42,7 @@ import java.nio.channels.ClosedChannelException;
|
|||
import java.nio.channels.DatagramChannel;
|
||||
import java.nio.channels.IllegalBlockingModeException;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
// Make a datagram-socket channel look like a datagram socket.
|
||||
|
@ -51,7 +52,7 @@ import java.util.Objects;
|
|||
// class.
|
||||
//
|
||||
|
||||
public class DatagramSocketAdaptor
|
||||
class DatagramSocketAdaptor
|
||||
extends DatagramSocket
|
||||
{
|
||||
// The channel being adapted
|
||||
|
@ -61,7 +62,7 @@ public class DatagramSocketAdaptor
|
|||
private volatile int timeout;
|
||||
|
||||
// ## super will create a useless impl
|
||||
private DatagramSocketAdaptor(DatagramChannelImpl dc) throws IOException {
|
||||
private DatagramSocketAdaptor(DatagramChannelImpl dc) {
|
||||
// Invoke the DatagramSocketAdaptor(SocketAddress) constructor,
|
||||
// passing a dummy DatagramSocketImpl object to avoid any native
|
||||
// resource allocation in super class and invoking our bind method
|
||||
|
@ -70,12 +71,8 @@ public class DatagramSocketAdaptor
|
|||
this.dc = dc;
|
||||
}
|
||||
|
||||
public static DatagramSocket create(DatagramChannelImpl dc) {
|
||||
try {
|
||||
return new DatagramSocketAdaptor(dc);
|
||||
} catch (IOException x) {
|
||||
throw new Error(x);
|
||||
}
|
||||
static DatagramSocket create(DatagramChannelImpl dc) {
|
||||
return new DatagramSocketAdaptor(dc);
|
||||
}
|
||||
|
||||
private void connectInternal(SocketAddress remote)
|
||||
|
@ -96,6 +93,7 @@ public class DatagramSocketAdaptor
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(SocketAddress local) throws SocketException {
|
||||
try {
|
||||
if (local == null)
|
||||
|
@ -106,6 +104,7 @@ public class DatagramSocketAdaptor
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect(InetAddress address, int port) {
|
||||
try {
|
||||
connectInternal(new InetSocketAddress(address, port));
|
||||
|
@ -114,11 +113,13 @@ public class DatagramSocketAdaptor
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect(SocketAddress remote) throws SocketException {
|
||||
Objects.requireNonNull(remote, "Address can't be null");
|
||||
connectInternal(remote);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
try {
|
||||
dc.disconnect();
|
||||
|
@ -127,24 +128,39 @@ public class DatagramSocketAdaptor
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBound() {
|
||||
return dc.localAddress() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return dc.remoteAddress() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetAddress getInetAddress() {
|
||||
InetSocketAddress remote = dc.remoteAddress();
|
||||
return (remote != null) ? remote.getAddress() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
InetSocketAddress remote = dc.remoteAddress();
|
||||
return (remote != null) ? remote.getPort() : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getRemoteSocketAddress() {
|
||||
return dc.remoteAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getLocalSocketAddress() {
|
||||
return dc.localAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(DatagramPacket p) throws IOException {
|
||||
synchronized (dc.blockingLock()) {
|
||||
if (!dc.isBlocking())
|
||||
|
@ -198,6 +214,7 @@ public class DatagramSocketAdaptor
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(DatagramPacket p) throws IOException {
|
||||
synchronized (dc.blockingLock()) {
|
||||
if (!dc.isBlocking())
|
||||
|
@ -217,6 +234,7 @@ public class DatagramSocketAdaptor
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetAddress getLocalAddress() {
|
||||
if (isClosed())
|
||||
return null;
|
||||
|
@ -235,6 +253,7 @@ public class DatagramSocketAdaptor
|
|||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLocalPort() {
|
||||
if (isClosed())
|
||||
return -1;
|
||||
|
@ -248,11 +267,19 @@ public class DatagramSocketAdaptor
|
|||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSoTimeout(int timeout) throws SocketException {
|
||||
if (!dc.isOpen())
|
||||
throw new SocketException("Socket is closed");
|
||||
if (timeout < 0)
|
||||
throw new IllegalArgumentException("timeout < 0");
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSoTimeout() throws SocketException {
|
||||
if (!dc.isOpen())
|
||||
throw new SocketException("Socket is closed");
|
||||
return timeout;
|
||||
}
|
||||
|
||||
|
@ -294,51 +321,62 @@ public class DatagramSocketAdaptor
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSendBufferSize(int size) throws SocketException {
|
||||
if (size <= 0)
|
||||
throw new IllegalArgumentException("Invalid send size");
|
||||
setIntOption(StandardSocketOptions.SO_SNDBUF, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSendBufferSize() throws SocketException {
|
||||
return getIntOption(StandardSocketOptions.SO_SNDBUF);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReceiveBufferSize(int size) throws SocketException {
|
||||
if (size <= 0)
|
||||
throw new IllegalArgumentException("Invalid receive size");
|
||||
setIntOption(StandardSocketOptions.SO_RCVBUF, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getReceiveBufferSize() throws SocketException {
|
||||
return getIntOption(StandardSocketOptions.SO_RCVBUF);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReuseAddress(boolean on) throws SocketException {
|
||||
setBooleanOption(StandardSocketOptions.SO_REUSEADDR, on);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getReuseAddress() throws SocketException {
|
||||
return getBooleanOption(StandardSocketOptions.SO_REUSEADDR);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBroadcast(boolean on) throws SocketException {
|
||||
setBooleanOption(StandardSocketOptions.SO_BROADCAST, on);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBroadcast() throws SocketException {
|
||||
return getBooleanOption(StandardSocketOptions.SO_BROADCAST);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTrafficClass(int tc) throws SocketException {
|
||||
setIntOption(StandardSocketOptions.IP_TOS, tc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTrafficClass() throws SocketException {
|
||||
return getIntOption(StandardSocketOptions.IP_TOS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
dc.close();
|
||||
|
@ -347,14 +385,32 @@ public class DatagramSocketAdaptor
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return !dc.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatagramChannel getChannel() {
|
||||
return dc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> DatagramSocket setOption(SocketOption<T> name, T value) throws IOException {
|
||||
dc.setOption(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getOption(SocketOption<T> name) throws IOException {
|
||||
return dc.getOption(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<SocketOption<?>> supportedOptions() {
|
||||
return dc.supportedOptions();
|
||||
}
|
||||
|
||||
/*
|
||||
* A dummy implementation of DatagramSocketImpl that can be passed to the
|
||||
* DatagramSocket constructor so that no native resources are allocated in
|
||||
|
|
153
src/java.base/share/classes/sun/nio/ch/DummySocketImpl.java
Normal file
153
src/java.base/share/classes/sun/nio/ch/DummySocketImpl.java
Normal file
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.ch;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.SocketImpl;
|
||||
import java.net.SocketOption;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Dummy SocketImpl for use by the socket adaptors. All methods are overridden
|
||||
* to throw an error.
|
||||
*/
|
||||
|
||||
class DummySocketImpl extends SocketImpl {
|
||||
private static final PrivilegedAction<SocketImpl> NEW = DummySocketImpl::new;
|
||||
|
||||
private DummySocketImpl() { }
|
||||
|
||||
static SocketImpl create() {
|
||||
return AccessController.doPrivileged(NEW);
|
||||
}
|
||||
|
||||
private static <T> T shouldNotGetHere() {
|
||||
throw new InternalError("Should not get here");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void create(boolean stream) {
|
||||
shouldNotGetHere();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void connect(SocketAddress remote, int millis) {
|
||||
shouldNotGetHere();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void connect(String host, int port) {
|
||||
shouldNotGetHere();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void connect(InetAddress address, int port) {
|
||||
shouldNotGetHere();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bind(InetAddress host, int port) {
|
||||
shouldNotGetHere();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void listen(int backlog) {
|
||||
shouldNotGetHere();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void accept(SocketImpl si) {
|
||||
shouldNotGetHere();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InputStream getInputStream() {
|
||||
return shouldNotGetHere();
|
||||
}
|
||||
@Override
|
||||
protected OutputStream getOutputStream() {
|
||||
return shouldNotGetHere();
|
||||
}
|
||||
@Override
|
||||
protected int available() {
|
||||
return shouldNotGetHere();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void close() {
|
||||
shouldNotGetHere();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<SocketOption<?>> supportedOptions() {
|
||||
return shouldNotGetHere();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> void setOption(SocketOption<T> opt, T value) {
|
||||
shouldNotGetHere();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> T getOption(SocketOption<T> opt) {
|
||||
return shouldNotGetHere();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOption(int opt, Object value) {
|
||||
shouldNotGetHere();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getOption(int opt) {
|
||||
return shouldNotGetHere();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutdownInput() {
|
||||
shouldNotGetHere();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutdownOutput() {
|
||||
shouldNotGetHere();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsUrgentData() {
|
||||
return shouldNotGetHere();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendUrgentData(int data) {
|
||||
shouldNotGetHere();
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2019, 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,6 +29,8 @@ import java.nio.channels.Channel;
|
|||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
||||
|
||||
/**
|
||||
* An interface that allows translation (and more!).
|
||||
*
|
||||
|
@ -68,4 +70,40 @@ public interface SelChImpl extends Channel {
|
|||
|
||||
void kill() throws IOException;
|
||||
|
||||
/**
|
||||
* Disables the current thread for scheduling purposes until this
|
||||
* channel is ready for I/O, or asynchronously closed, for up to the
|
||||
* specified waiting time.
|
||||
*
|
||||
* <p> This method does <em>not</em> report which of these caused the
|
||||
* method to return. Callers should re-check the conditions which caused
|
||||
* the thread to park.
|
||||
*
|
||||
* @param event the event to poll
|
||||
* @param nanos the timeout to wait; {@code <= 0} to wait indefinitely
|
||||
*/
|
||||
default void park(int event, long nanos) throws IOException {
|
||||
long millis;
|
||||
if (nanos <= 0) {
|
||||
millis = -1;
|
||||
} else {
|
||||
millis = NANOSECONDS.toMillis(nanos);
|
||||
}
|
||||
Net.poll(getFD(), event, millis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the current thread for scheduling purposes until this
|
||||
* channel is ready for I/O, or asynchronously closed.
|
||||
*
|
||||
* <p> This method does <em>not</em> report which of these caused the
|
||||
* method to return. Callers should re-check the conditions which caused
|
||||
* the thread to park.
|
||||
*
|
||||
* @param event the event to poll
|
||||
*/
|
||||
default void park(int event) throws IOException {
|
||||
park(event, 0L);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -32,12 +32,14 @@ import java.net.ServerSocket;
|
|||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.SocketOption;
|
||||
import java.net.StandardSocketOptions;
|
||||
import java.nio.channels.IllegalBlockingModeException;
|
||||
import java.nio.channels.NotYetBoundException;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
|
||||
// Make a server-socket channel look like a server socket.
|
||||
|
@ -56,23 +58,21 @@ class ServerSocketAdaptor // package-private
|
|||
// Timeout "option" value for accepts
|
||||
private volatile int timeout;
|
||||
|
||||
public static ServerSocket create(ServerSocketChannelImpl ssc) {
|
||||
try {
|
||||
return new ServerSocketAdaptor(ssc);
|
||||
} catch (IOException x) {
|
||||
throw new Error(x);
|
||||
}
|
||||
static ServerSocket create(ServerSocketChannelImpl ssc) {
|
||||
return new ServerSocketAdaptor(ssc);
|
||||
}
|
||||
|
||||
// ## super will create a useless impl
|
||||
private ServerSocketAdaptor(ServerSocketChannelImpl ssc) throws IOException {
|
||||
private ServerSocketAdaptor(ServerSocketChannelImpl ssc) {
|
||||
super(DummySocketImpl.create());
|
||||
this.ssc = ssc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(SocketAddress local) throws IOException {
|
||||
bind(local, 50);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(SocketAddress local, int backlog) throws IOException {
|
||||
if (local == null)
|
||||
local = new InetSocketAddress(0);
|
||||
|
@ -83,6 +83,7 @@ class ServerSocketAdaptor // package-private
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetAddress getInetAddress() {
|
||||
InetSocketAddress local = ssc.localAddress();
|
||||
if (local == null) {
|
||||
|
@ -92,6 +93,7 @@ class ServerSocketAdaptor // package-private
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLocalPort() {
|
||||
InetSocketAddress local = ssc.localAddress();
|
||||
if (local == null) {
|
||||
|
@ -101,65 +103,65 @@ class ServerSocketAdaptor // package-private
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket accept() throws IOException {
|
||||
synchronized (ssc.blockingLock()) {
|
||||
try {
|
||||
if (!ssc.isBound())
|
||||
throw new NotYetBoundException();
|
||||
|
||||
long to = this.timeout;
|
||||
if (to == 0) {
|
||||
// for compatibility reasons: accept connection if available
|
||||
// when configured non-blocking
|
||||
SocketChannel sc = ssc.accept();
|
||||
if (sc == null && !ssc.isBlocking())
|
||||
throw new IllegalBlockingModeException();
|
||||
return sc.socket();
|
||||
}
|
||||
|
||||
if (!ssc.isBlocking())
|
||||
SocketChannel sc = null;
|
||||
try {
|
||||
int timeout = this.timeout;
|
||||
if (timeout > 0) {
|
||||
long nanos = MILLISECONDS.toNanos(timeout);
|
||||
sc = ssc.blockingAccept(nanos);
|
||||
} else {
|
||||
// accept connection if possible when non-blocking (to preserve
|
||||
// long standing behavior)
|
||||
sc = ssc.accept();
|
||||
if (sc == null) {
|
||||
throw new IllegalBlockingModeException();
|
||||
for (;;) {
|
||||
long st = System.currentTimeMillis();
|
||||
if (ssc.pollAccept(to))
|
||||
return ssc.accept().socket();
|
||||
to -= System.currentTimeMillis() - st;
|
||||
if (to <= 0)
|
||||
throw new SocketTimeoutException();
|
||||
}
|
||||
|
||||
} catch (Exception x) {
|
||||
Net.translateException(x);
|
||||
assert false;
|
||||
return null; // Never happens
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Net.translateException(e);
|
||||
}
|
||||
return sc.socket();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
ssc.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerSocketChannel getChannel() {
|
||||
return ssc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBound() {
|
||||
return ssc.isBound();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return !ssc.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSoTimeout(int timeout) throws SocketException {
|
||||
if (!ssc.isOpen())
|
||||
throw new SocketException("Socket is closed");
|
||||
if (timeout < 0)
|
||||
throw new IllegalArgumentException("timeout < 0");
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSoTimeout() throws SocketException {
|
||||
if (!ssc.isOpen())
|
||||
throw new SocketException("Socket is closed");
|
||||
return timeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReuseAddress(boolean on) throws SocketException {
|
||||
try {
|
||||
ssc.setOption(StandardSocketOptions.SO_REUSEADDR, on);
|
||||
|
@ -168,6 +170,7 @@ class ServerSocketAdaptor // package-private
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getReuseAddress() throws SocketException {
|
||||
try {
|
||||
return ssc.getOption(StandardSocketOptions.SO_REUSEADDR).booleanValue();
|
||||
|
@ -177,6 +180,7 @@ class ServerSocketAdaptor // package-private
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (!isBound())
|
||||
return "ServerSocket[unbound]";
|
||||
|
@ -184,6 +188,7 @@ class ServerSocketAdaptor // package-private
|
|||
",localport=" + getLocalPort() + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReceiveBufferSize(int size) throws SocketException {
|
||||
// size 0 valid for ServerSocketChannel, invalid for ServerSocket
|
||||
if (size <= 0)
|
||||
|
@ -195,6 +200,7 @@ class ServerSocketAdaptor // package-private
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getReceiveBufferSize() throws SocketException {
|
||||
try {
|
||||
return ssc.getOption(StandardSocketOptions.SO_RCVBUF).intValue();
|
||||
|
@ -203,4 +209,20 @@ class ServerSocketAdaptor // package-private
|
|||
return -1; // Never happens
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ServerSocket setOption(SocketOption<T> name, T value) throws IOException {
|
||||
ssc.setOption(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getOption(SocketOption<T> name) throws IOException {
|
||||
return ssc.getOption(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<SocketOption<?>> supportedOptions() {
|
||||
return ssc.supportedOptions();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,10 +31,12 @@ import java.net.InetSocketAddress;
|
|||
import java.net.ServerSocket;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.SocketOption;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.StandardSocketOptions;
|
||||
import java.nio.channels.AlreadyBoundException;
|
||||
import java.nio.channels.AsynchronousCloseException;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.channels.IllegalBlockingModeException;
|
||||
import java.nio.channels.NotYetBoundException;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
|
@ -44,6 +46,7 @@ import java.util.Collections;
|
|||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import sun.net.NetHooks;
|
||||
|
@ -69,7 +72,8 @@ class ServerSocketChannelImpl
|
|||
|
||||
// Lock held by any thread that modifies the state fields declared below
|
||||
// DO NOT invoke a blocking I/O operation while holding this lock!
|
||||
private final Object stateLock = new Object();
|
||||
private final ReentrantLock stateLock = new ReentrantLock();
|
||||
private final Condition stateCondition = stateLock.newCondition();
|
||||
|
||||
// -- The following fields are protected by stateLock
|
||||
|
||||
|
@ -95,7 +99,7 @@ class ServerSocketChannelImpl
|
|||
// -- End of fields protected by stateLock
|
||||
|
||||
|
||||
ServerSocketChannelImpl(SelectorProvider sp) throws IOException {
|
||||
ServerSocketChannelImpl(SelectorProvider sp) {
|
||||
super(sp);
|
||||
this.fd = Net.serverSocket(true);
|
||||
this.fdVal = IOUtil.fdVal(fd);
|
||||
|
@ -108,8 +112,11 @@ class ServerSocketChannelImpl
|
|||
this.fd = fd;
|
||||
this.fdVal = IOUtil.fdVal(fd);
|
||||
if (bound) {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
localAddress = Net.localAddress(fd);
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -122,20 +129,26 @@ class ServerSocketChannelImpl
|
|||
|
||||
@Override
|
||||
public ServerSocket socket() {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
if (socket == null)
|
||||
socket = ServerSocketAdaptor.create(this);
|
||||
return socket;
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getLocalAddress() throws IOException {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
return (localAddress == null)
|
||||
? null
|
||||
: Net.getRevealedLocalAddress(localAddress);
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,7 +159,8 @@ class ServerSocketChannelImpl
|
|||
Objects.requireNonNull(name);
|
||||
if (!supportedOptions().contains(name))
|
||||
throw new UnsupportedOperationException("'" + name + "' not supported");
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
|
||||
if (name == StandardSocketOptions.SO_REUSEADDR && Net.useExclusiveBind()) {
|
||||
|
@ -157,6 +171,8 @@ class ServerSocketChannelImpl
|
|||
Net.setSocketOption(fd, Net.UNSPEC, name, value);
|
||||
}
|
||||
return this;
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -169,7 +185,8 @@ class ServerSocketChannelImpl
|
|||
if (!supportedOptions().contains(name))
|
||||
throw new UnsupportedOperationException("'" + name + "' not supported");
|
||||
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
if (name == StandardSocketOptions.SO_REUSEADDR && Net.useExclusiveBind()) {
|
||||
// SO_REUSEADDR emulated when using exclusive bind
|
||||
|
@ -177,6 +194,8 @@ class ServerSocketChannelImpl
|
|||
}
|
||||
// no options that require special handling
|
||||
return (T) Net.getSocketOption(fd, Net.UNSPEC, name);
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -202,7 +221,8 @@ class ServerSocketChannelImpl
|
|||
|
||||
@Override
|
||||
public ServerSocketChannel bind(SocketAddress local, int backlog) throws IOException {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
if (localAddress != null)
|
||||
throw new AlreadyBoundException();
|
||||
|
@ -216,6 +236,8 @@ class ServerSocketChannelImpl
|
|||
Net.bind(fd, isa.getAddress(), isa.getPort());
|
||||
Net.listen(fd, backlog < 1 ? 50 : backlog);
|
||||
localAddress = Net.localAddress(fd);
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
@ -229,12 +251,15 @@ class ServerSocketChannelImpl
|
|||
private void begin(boolean blocking) throws ClosedChannelException {
|
||||
if (blocking)
|
||||
begin(); // set blocker to close channel if interrupted
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
if (localAddress == null)
|
||||
throw new NotYetBoundException();
|
||||
if (blocking)
|
||||
thread = NativeThread.current();
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -248,12 +273,15 @@ class ServerSocketChannelImpl
|
|||
throws AsynchronousCloseException
|
||||
{
|
||||
if (blocking) {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
thread = 0;
|
||||
// notify any thread waiting in implCloseSelectableChannel
|
||||
if (state == ST_CLOSING) {
|
||||
stateLock.notifyAll();
|
||||
stateCondition.signalAll();
|
||||
}
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
end(completed);
|
||||
}
|
||||
|
@ -270,22 +298,82 @@ class ServerSocketChannelImpl
|
|||
boolean blocking = isBlocking();
|
||||
try {
|
||||
begin(blocking);
|
||||
do {
|
||||
n = Net.accept(this.fd, newfd, isaa);
|
||||
} while (n == IOStatus.INTERRUPTED && isOpen());
|
||||
n = Net.accept(this.fd, newfd, isaa);
|
||||
if (blocking) {
|
||||
while (IOStatus.okayToRetry(n) && isOpen()) {
|
||||
park(Net.POLLIN);
|
||||
n = Net.accept(this.fd, newfd, isaa);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
end(blocking, n > 0);
|
||||
assert IOStatus.check(n);
|
||||
}
|
||||
|
||||
} finally {
|
||||
acceptLock.unlock();
|
||||
}
|
||||
|
||||
if (n < 1)
|
||||
if (n > 0) {
|
||||
return finishAccept(newfd, isaa[0]);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
InetSocketAddress isa = isaa[0];
|
||||
/**
|
||||
* Accepts a new connection with a given timeout. This method requires the
|
||||
* channel to be configured in blocking mode.
|
||||
*
|
||||
* @apiNote This method is for use by the socket adaptor.
|
||||
*
|
||||
* @param nanos the timeout, in nanoseconds
|
||||
* @throws IllegalBlockingModeException if the channel is configured non-blocking
|
||||
* @throws SocketTimeoutException if the timeout expires
|
||||
*/
|
||||
SocketChannel blockingAccept(long nanos) throws IOException {
|
||||
int n = 0;
|
||||
FileDescriptor newfd = new FileDescriptor();
|
||||
InetSocketAddress[] isaa = new InetSocketAddress[1];
|
||||
|
||||
acceptLock.lock();
|
||||
try {
|
||||
// check that channel is configured blocking
|
||||
if (!isBlocking())
|
||||
throw new IllegalBlockingModeException();
|
||||
|
||||
try {
|
||||
begin(true);
|
||||
// change socket to non-blocking
|
||||
lockedConfigureBlocking(false);
|
||||
try {
|
||||
long startNanos = System.nanoTime();
|
||||
n = Net.accept(fd, newfd, isaa);
|
||||
while (n == IOStatus.UNAVAILABLE && isOpen()) {
|
||||
long remainingNanos = nanos - (System.nanoTime() - startNanos);
|
||||
if (remainingNanos <= 0) {
|
||||
throw new SocketTimeoutException("Accept timed out");
|
||||
}
|
||||
park(Net.POLLIN, remainingNanos);
|
||||
n = Net.accept(fd, newfd, isaa);
|
||||
}
|
||||
} finally {
|
||||
// restore socket to blocking mode
|
||||
lockedConfigureBlocking(true);
|
||||
}
|
||||
} finally {
|
||||
end(true, n > 0);
|
||||
}
|
||||
} finally {
|
||||
acceptLock.unlock();
|
||||
}
|
||||
|
||||
assert n > 0;
|
||||
return finishAccept(newfd, isaa[0]);
|
||||
}
|
||||
|
||||
private SocketChannel finishAccept(FileDescriptor newfd, InetSocketAddress isa)
|
||||
throws IOException
|
||||
{
|
||||
try {
|
||||
// newly accepted socket is initially in blocking mode
|
||||
IOUtil.configureBlocking(newfd, true);
|
||||
|
@ -306,15 +394,26 @@ class ServerSocketChannelImpl
|
|||
protected void implConfigureBlocking(boolean block) throws IOException {
|
||||
acceptLock.lock();
|
||||
try {
|
||||
synchronized (stateLock) {
|
||||
ensureOpen();
|
||||
IOUtil.configureBlocking(fd, block);
|
||||
}
|
||||
lockedConfigureBlocking(block);
|
||||
} finally {
|
||||
acceptLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the blocking mode while holding acceptLock.
|
||||
*/
|
||||
private void lockedConfigureBlocking(boolean block) throws IOException {
|
||||
assert acceptLock.isHeldByCurrentThread();
|
||||
stateLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
IOUtil.configureBlocking(fd, block);
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked by implCloseChannel to close the channel.
|
||||
*
|
||||
|
@ -336,15 +435,19 @@ class ServerSocketChannelImpl
|
|||
boolean blocking;
|
||||
|
||||
// set state to ST_CLOSING
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
assert state < ST_CLOSING;
|
||||
state = ST_CLOSING;
|
||||
blocking = isBlocking();
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
|
||||
// wait for any outstanding accept to complete
|
||||
if (blocking) {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
assert state == ST_CLOSING;
|
||||
long th = thread;
|
||||
if (th != 0) {
|
||||
|
@ -354,12 +457,14 @@ class ServerSocketChannelImpl
|
|||
// wait for accept operation to end
|
||||
while (thread != 0) {
|
||||
try {
|
||||
stateLock.wait();
|
||||
stateCondition.await();
|
||||
} catch (InterruptedException e) {
|
||||
interrupted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
} else {
|
||||
// non-blocking mode: wait for accept to complete
|
||||
|
@ -368,9 +473,12 @@ class ServerSocketChannelImpl
|
|||
}
|
||||
|
||||
// set state to ST_KILLPENDING
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
assert state == ST_CLOSING;
|
||||
state = ST_KILLPENDING;
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
|
||||
// close socket if not registered with Selector
|
||||
|
@ -384,11 +492,14 @@ class ServerSocketChannelImpl
|
|||
|
||||
@Override
|
||||
public void kill() throws IOException {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
if (state == ST_KILLPENDING) {
|
||||
state = ST_KILLED;
|
||||
nd.close(fd);
|
||||
}
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -396,8 +507,11 @@ class ServerSocketChannelImpl
|
|||
* Returns true if channel's socket is bound
|
||||
*/
|
||||
boolean isBound() {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
return localAddress != null;
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -405,30 +519,11 @@ class ServerSocketChannelImpl
|
|||
* Returns the local address, or null if not bound
|
||||
*/
|
||||
InetSocketAddress localAddress() {
|
||||
synchronized (stateLock) {
|
||||
return localAddress;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Poll this channel's socket for a new connection up to the given timeout.
|
||||
* @return {@code true} if there is a connection to accept
|
||||
*/
|
||||
boolean pollAccept(long timeout) throws IOException {
|
||||
assert Thread.holdsLock(blockingLock()) && isBlocking();
|
||||
acceptLock.lock();
|
||||
stateLock.lock();
|
||||
try {
|
||||
boolean polled = false;
|
||||
try {
|
||||
begin(true);
|
||||
int events = Net.poll(fd, Net.POLLIN, timeout);
|
||||
polled = (events != 0);
|
||||
} finally {
|
||||
end(true, polled);
|
||||
}
|
||||
return polled;
|
||||
return localAddress;
|
||||
} finally {
|
||||
acceptLock.unlock();
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -494,13 +589,16 @@ class ServerSocketChannelImpl
|
|||
if (!isOpen()) {
|
||||
sb.append("closed");
|
||||
} else {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
InetSocketAddress addr = localAddress;
|
||||
if (addr == null) {
|
||||
sb.append("unbound");
|
||||
} else {
|
||||
sb.append(Net.getRevealedLocalAddressAsString(addr));
|
||||
}
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
sb.append(']');
|
||||
|
|
|
@ -33,18 +33,12 @@ import java.net.InetSocketAddress;
|
|||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.net.SocketImpl;
|
||||
import java.net.SocketOption;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.StandardSocketOptions;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.channels.IllegalBlockingModeException;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import static java.util.concurrent.TimeUnit.*;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
// Make a socket channel look like a socket.
|
||||
//
|
||||
|
@ -62,11 +56,11 @@ class SocketAdaptor
|
|||
private volatile int timeout;
|
||||
|
||||
private SocketAdaptor(SocketChannelImpl sc) throws SocketException {
|
||||
super((SocketImpl) null);
|
||||
super(DummySocketImpl.create());
|
||||
this.sc = sc;
|
||||
}
|
||||
|
||||
public static Socket create(SocketChannelImpl sc) {
|
||||
static Socket create(SocketChannelImpl sc) {
|
||||
try {
|
||||
return new SocketAdaptor(sc);
|
||||
} catch (SocketException e) {
|
||||
|
@ -74,70 +68,30 @@ class SocketAdaptor
|
|||
}
|
||||
}
|
||||
|
||||
public SocketChannel getChannel() {
|
||||
return sc;
|
||||
}
|
||||
|
||||
// Override this method just to protect against changes in the superclass
|
||||
//
|
||||
@Override
|
||||
public void connect(SocketAddress remote) throws IOException {
|
||||
connect(remote, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect(SocketAddress remote, int timeout) throws IOException {
|
||||
if (remote == null)
|
||||
throw new IllegalArgumentException("connect: The address can't be null");
|
||||
if (timeout < 0)
|
||||
throw new IllegalArgumentException("connect: timeout can't be negative");
|
||||
|
||||
synchronized (sc.blockingLock()) {
|
||||
if (!sc.isBlocking())
|
||||
throw new IllegalBlockingModeException();
|
||||
|
||||
try {
|
||||
// no timeout
|
||||
if (timeout == 0) {
|
||||
sc.connect(remote);
|
||||
return;
|
||||
}
|
||||
|
||||
// timed connect
|
||||
sc.configureBlocking(false);
|
||||
try {
|
||||
if (sc.connect(remote))
|
||||
return;
|
||||
} finally {
|
||||
try {
|
||||
sc.configureBlocking(true);
|
||||
} catch (ClosedChannelException e) { }
|
||||
}
|
||||
|
||||
long timeoutNanos = NANOSECONDS.convert(timeout, MILLISECONDS);
|
||||
long to = timeout;
|
||||
for (;;) {
|
||||
long startTime = System.nanoTime();
|
||||
if (sc.pollConnected(to)) {
|
||||
boolean connected = sc.finishConnect();
|
||||
assert connected;
|
||||
break;
|
||||
}
|
||||
timeoutNanos -= System.nanoTime() - startTime;
|
||||
if (timeoutNanos <= 0) {
|
||||
try {
|
||||
sc.close();
|
||||
} catch (IOException x) { }
|
||||
throw new SocketTimeoutException();
|
||||
}
|
||||
to = MILLISECONDS.convert(timeoutNanos, NANOSECONDS);
|
||||
}
|
||||
|
||||
} catch (Exception x) {
|
||||
Net.translateException(x, true);
|
||||
try {
|
||||
if (timeout > 0) {
|
||||
long nanos = MILLISECONDS.toNanos(timeout);
|
||||
sc.blockingConnect(remote, nanos);
|
||||
} else {
|
||||
sc.blockingConnect(remote, Long.MAX_VALUE);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Net.translateException(e, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(SocketAddress local) throws IOException {
|
||||
try {
|
||||
sc.bind(local);
|
||||
|
@ -146,6 +100,7 @@ class SocketAdaptor
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetAddress getInetAddress() {
|
||||
InetSocketAddress remote = sc.remoteAddress();
|
||||
if (remote == null) {
|
||||
|
@ -155,6 +110,7 @@ class SocketAdaptor
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetAddress getLocalAddress() {
|
||||
if (sc.isOpen()) {
|
||||
InetSocketAddress local = sc.localAddress();
|
||||
|
@ -165,6 +121,7 @@ class SocketAdaptor
|
|||
return new InetSocketAddress(0).getAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
InetSocketAddress remote = sc.remoteAddress();
|
||||
if (remote == null) {
|
||||
|
@ -174,6 +131,7 @@ class SocketAdaptor
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLocalPort() {
|
||||
InetSocketAddress local = sc.localAddress();
|
||||
if (local == null) {
|
||||
|
@ -183,48 +141,27 @@ class SocketAdaptor
|
|||
}
|
||||
}
|
||||
|
||||
private class SocketInputStream
|
||||
extends ChannelInputStream
|
||||
{
|
||||
private SocketInputStream() {
|
||||
super(sc);
|
||||
}
|
||||
@Override
|
||||
public SocketAddress getRemoteSocketAddress() {
|
||||
return sc.remoteAddress();
|
||||
}
|
||||
|
||||
protected int read(ByteBuffer bb)
|
||||
throws IOException
|
||||
{
|
||||
synchronized (sc.blockingLock()) {
|
||||
if (!sc.isBlocking())
|
||||
throw new IllegalBlockingModeException();
|
||||
|
||||
// no timeout
|
||||
long to = SocketAdaptor.this.timeout;
|
||||
if (to == 0)
|
||||
return sc.read(bb);
|
||||
|
||||
// timed read
|
||||
long timeoutNanos = NANOSECONDS.convert(to, MILLISECONDS);
|
||||
for (;;) {
|
||||
long startTime = System.nanoTime();
|
||||
if (sc.pollRead(to)) {
|
||||
return sc.read(bb);
|
||||
}
|
||||
timeoutNanos -= System.nanoTime() - startTime;
|
||||
if (timeoutNanos <= 0)
|
||||
throw new SocketTimeoutException();
|
||||
to = MILLISECONDS.convert(timeoutNanos, NANOSECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
return sc.available();
|
||||
@Override
|
||||
public SocketAddress getLocalSocketAddress() {
|
||||
InetSocketAddress local = sc.localAddress();
|
||||
if (local != null) {
|
||||
return Net.getRevealedLocalAddress(local);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream socketInputStream = null;
|
||||
@Override
|
||||
public SocketChannel getChannel() {
|
||||
return sc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
if (!sc.isOpen())
|
||||
throw new SocketException("Socket is closed");
|
||||
|
@ -232,21 +169,35 @@ class SocketAdaptor
|
|||
throw new SocketException("Socket is not connected");
|
||||
if (!sc.isInputOpen())
|
||||
throw new SocketException("Socket input is shutdown");
|
||||
if (socketInputStream == null) {
|
||||
try {
|
||||
socketInputStream = AccessController.doPrivileged(
|
||||
new PrivilegedExceptionAction<InputStream>() {
|
||||
public InputStream run() throws IOException {
|
||||
return new SocketInputStream();
|
||||
}
|
||||
});
|
||||
} catch (java.security.PrivilegedActionException e) {
|
||||
throw (IOException)e.getException();
|
||||
return new InputStream() {
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
byte[] a = new byte[1];
|
||||
int n = read(a, 0, 1);
|
||||
return (n > 0) ? (a[0] & 0xff) : -1;
|
||||
}
|
||||
}
|
||||
return socketInputStream;
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
int timeout = SocketAdaptor.this.timeout;
|
||||
if (timeout > 0) {
|
||||
long nanos = MILLISECONDS.toNanos(timeout);
|
||||
return sc.blockingRead(b, off, len, nanos);
|
||||
} else {
|
||||
return sc.blockingRead(b, off, len, 0);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
return sc.available();
|
||||
}
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
sc.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream getOutputStream() throws IOException {
|
||||
if (!sc.isOpen())
|
||||
throw new SocketException("Socket is closed");
|
||||
|
@ -254,18 +205,21 @@ class SocketAdaptor
|
|||
throw new SocketException("Socket is not connected");
|
||||
if (!sc.isOutputOpen())
|
||||
throw new SocketException("Socket output is shutdown");
|
||||
OutputStream os = null;
|
||||
try {
|
||||
os = AccessController.doPrivileged(
|
||||
new PrivilegedExceptionAction<OutputStream>() {
|
||||
public OutputStream run() throws IOException {
|
||||
return Channels.newOutputStream(sc);
|
||||
}
|
||||
});
|
||||
} catch (java.security.PrivilegedActionException e) {
|
||||
throw (IOException)e.getException();
|
||||
}
|
||||
return os;
|
||||
return new OutputStream() {
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
byte[] a = new byte[]{(byte) b};
|
||||
write(a, 0, 1);
|
||||
}
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
sc.blockingWriteFully(b, off, len);
|
||||
}
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
sc.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void setBooleanOption(SocketOption<Boolean> name, boolean value)
|
||||
|
@ -306,48 +260,62 @@ class SocketAdaptor
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTcpNoDelay(boolean on) throws SocketException {
|
||||
setBooleanOption(StandardSocketOptions.TCP_NODELAY, on);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getTcpNoDelay() throws SocketException {
|
||||
return getBooleanOption(StandardSocketOptions.TCP_NODELAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSoLinger(boolean on, int linger) throws SocketException {
|
||||
if (!on)
|
||||
linger = -1;
|
||||
setIntOption(StandardSocketOptions.SO_LINGER, linger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSoLinger() throws SocketException {
|
||||
return getIntOption(StandardSocketOptions.SO_LINGER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendUrgentData(int data) throws IOException {
|
||||
int n = sc.sendOutOfBandData((byte) data);
|
||||
if (n == 0)
|
||||
throw new IOException("Socket buffer full");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOOBInline(boolean on) throws SocketException {
|
||||
setBooleanOption(ExtendedSocketOption.SO_OOBINLINE, on);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getOOBInline() throws SocketException {
|
||||
return getBooleanOption(ExtendedSocketOption.SO_OOBINLINE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSoTimeout(int timeout) throws SocketException {
|
||||
if (!sc.isOpen())
|
||||
throw new SocketException("Socket is closed");
|
||||
if (timeout < 0)
|
||||
throw new IllegalArgumentException("timeout can't be negative");
|
||||
throw new IllegalArgumentException("timeout < 0");
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSoTimeout() throws SocketException {
|
||||
if (!sc.isOpen())
|
||||
throw new SocketException("Socket is closed");
|
||||
return timeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSendBufferSize(int size) throws SocketException {
|
||||
// size 0 valid for SocketChannel, invalid for Socket
|
||||
if (size <= 0)
|
||||
|
@ -355,10 +323,12 @@ class SocketAdaptor
|
|||
setIntOption(StandardSocketOptions.SO_SNDBUF, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSendBufferSize() throws SocketException {
|
||||
return getIntOption(StandardSocketOptions.SO_SNDBUF);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReceiveBufferSize(int size) throws SocketException {
|
||||
// size 0 valid for SocketChannel, invalid for Socket
|
||||
if (size <= 0)
|
||||
|
@ -366,38 +336,47 @@ class SocketAdaptor
|
|||
setIntOption(StandardSocketOptions.SO_RCVBUF, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getReceiveBufferSize() throws SocketException {
|
||||
return getIntOption(StandardSocketOptions.SO_RCVBUF);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKeepAlive(boolean on) throws SocketException {
|
||||
setBooleanOption(StandardSocketOptions.SO_KEEPALIVE, on);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getKeepAlive() throws SocketException {
|
||||
return getBooleanOption(StandardSocketOptions.SO_KEEPALIVE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTrafficClass(int tc) throws SocketException {
|
||||
setIntOption(StandardSocketOptions.IP_TOS, tc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTrafficClass() throws SocketException {
|
||||
return getIntOption(StandardSocketOptions.IP_TOS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReuseAddress(boolean on) throws SocketException {
|
||||
setBooleanOption(StandardSocketOptions.SO_REUSEADDR, on);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getReuseAddress() throws SocketException {
|
||||
return getBooleanOption(StandardSocketOptions.SO_REUSEADDR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
sc.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdownInput() throws IOException {
|
||||
try {
|
||||
sc.shutdownInput();
|
||||
|
@ -406,6 +385,7 @@ class SocketAdaptor
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdownOutput() throws IOException {
|
||||
try {
|
||||
sc.shutdownOutput();
|
||||
|
@ -414,6 +394,7 @@ class SocketAdaptor
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (sc.isConnected())
|
||||
return "Socket[addr=" + getInetAddress() +
|
||||
|
@ -422,23 +403,44 @@ class SocketAdaptor
|
|||
return "Socket[unconnected]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return sc.isConnected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBound() {
|
||||
return sc.localAddress() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return !sc.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInputShutdown() {
|
||||
return !sc.isInputOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOutputShutdown() {
|
||||
return !sc.isOutputOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Socket setOption(SocketOption<T> name, T value) throws IOException {
|
||||
sc.setOption(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getOption(SocketOption<T> name) throws IOException {
|
||||
return sc.getOption(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<SocketOption<?>> supportedOptions() {
|
||||
return sc.supportedOptions();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ import java.net.Socket;
|
|||
import java.net.SocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.net.SocketOption;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.StandardProtocolFamily;
|
||||
import java.net.StandardSocketOptions;
|
||||
import java.nio.ByteBuffer;
|
||||
|
@ -42,6 +43,7 @@ import java.nio.channels.AlreadyConnectedException;
|
|||
import java.nio.channels.AsynchronousCloseException;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.channels.ConnectionPendingException;
|
||||
import java.nio.channels.IllegalBlockingModeException;
|
||||
import java.nio.channels.NoConnectionPendingException;
|
||||
import java.nio.channels.NotYetConnectedException;
|
||||
import java.nio.channels.SelectionKey;
|
||||
|
@ -51,6 +53,7 @@ import java.util.Collections;
|
|||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import sun.net.ConnectionResetException;
|
||||
|
@ -81,7 +84,8 @@ class SocketChannelImpl
|
|||
|
||||
// Lock held by any thread that modifies the state fields declared below
|
||||
// DO NOT invoke a blocking I/O operation while holding this lock!
|
||||
private final Object stateLock = new Object();
|
||||
private final ReentrantLock stateLock = new ReentrantLock();
|
||||
private final Condition stateCondition = stateLock.newCondition();
|
||||
|
||||
// Input/Output closed
|
||||
private volatile boolean isInputClosed;
|
||||
|
@ -133,8 +137,11 @@ class SocketChannelImpl
|
|||
this.fd = fd;
|
||||
this.fdVal = IOUtil.fdVal(fd);
|
||||
if (bound) {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
this.localAddress = Net.localAddress(fd);
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -147,10 +154,13 @@ class SocketChannelImpl
|
|||
super(sp);
|
||||
this.fd = fd;
|
||||
this.fdVal = IOUtil.fdVal(fd);
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
this.localAddress = Net.localAddress(fd);
|
||||
this.remoteAddress = isa;
|
||||
this.state = ST_CONNECTED;
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -187,26 +197,35 @@ class SocketChannelImpl
|
|||
|
||||
@Override
|
||||
public Socket socket() {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
if (socket == null)
|
||||
socket = SocketAdaptor.create(this);
|
||||
return socket;
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getLocalAddress() throws IOException {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
return Net.getRevealedLocalAddress(localAddress);
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getRemoteAddress() throws IOException {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
return remoteAddress;
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -218,7 +237,8 @@ class SocketChannelImpl
|
|||
if (!supportedOptions().contains(name))
|
||||
throw new UnsupportedOperationException("'" + name + "' not supported");
|
||||
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
|
||||
if (name == StandardSocketOptions.IP_TOS) {
|
||||
|
@ -237,6 +257,8 @@ class SocketChannelImpl
|
|||
// no options that require special handling
|
||||
Net.setSocketOption(fd, name, value);
|
||||
return this;
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -249,7 +271,8 @@ class SocketChannelImpl
|
|||
if (!supportedOptions().contains(name))
|
||||
throw new UnsupportedOperationException("'" + name + "' not supported");
|
||||
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
|
||||
if (name == StandardSocketOptions.SO_REUSEADDR && Net.useExclusiveBind()) {
|
||||
|
@ -266,6 +289,8 @@ class SocketChannelImpl
|
|||
|
||||
// no options that require special handling
|
||||
return (T) Net.getSocketOption(fd, name);
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -307,10 +332,13 @@ class SocketChannelImpl
|
|||
// set hook for Thread.interrupt
|
||||
begin();
|
||||
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
ensureOpenAndConnected();
|
||||
// record thread so it can be signalled if needed
|
||||
readerThread = NativeThread.current();
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
} else {
|
||||
ensureOpenAndConnected();
|
||||
|
@ -327,12 +355,15 @@ class SocketChannelImpl
|
|||
throws AsynchronousCloseException
|
||||
{
|
||||
if (blocking) {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
readerThread = 0;
|
||||
// notify any thread waiting in implCloseSelectableChannel
|
||||
if (state == ST_CLOSING) {
|
||||
stateLock.notifyAll();
|
||||
stateCondition.signalAll();
|
||||
}
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
// remove hook for Thread.interrupt
|
||||
end(completed);
|
||||
|
@ -362,12 +393,12 @@ class SocketChannelImpl
|
|||
if (isInputClosed)
|
||||
return IOStatus.EOF;
|
||||
|
||||
n = IOUtil.read(fd, buf, -1, nd);
|
||||
if (blocking) {
|
||||
do {
|
||||
while (IOStatus.okayToRetry(n) && isOpen()) {
|
||||
park(Net.POLLIN);
|
||||
n = IOUtil.read(fd, buf, -1, nd);
|
||||
} while (n == IOStatus.INTERRUPTED && isOpen());
|
||||
} else {
|
||||
n = IOUtil.read(fd, buf, -1, nd);
|
||||
}
|
||||
}
|
||||
} catch (ConnectionResetException e) {
|
||||
connectionReset = true;
|
||||
|
@ -404,12 +435,12 @@ class SocketChannelImpl
|
|||
if (isInputClosed)
|
||||
return IOStatus.EOF;
|
||||
|
||||
n = IOUtil.read(fd, dsts, offset, length, nd);
|
||||
if (blocking) {
|
||||
do {
|
||||
while (IOStatus.okayToRetry(n) && isOpen()) {
|
||||
park(Net.POLLIN);
|
||||
n = IOUtil.read(fd, dsts, offset, length, nd);
|
||||
} while (n == IOStatus.INTERRUPTED && isOpen());
|
||||
} else {
|
||||
n = IOUtil.read(fd, dsts, offset, length, nd);
|
||||
}
|
||||
}
|
||||
} catch (ConnectionResetException e) {
|
||||
connectionReset = true;
|
||||
|
@ -436,12 +467,15 @@ class SocketChannelImpl
|
|||
// set hook for Thread.interrupt
|
||||
begin();
|
||||
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
ensureOpenAndConnected();
|
||||
if (isOutputClosed)
|
||||
throw new ClosedChannelException();
|
||||
// record thread so it can be signalled if needed
|
||||
writerThread = NativeThread.current();
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
} else {
|
||||
ensureOpenAndConnected();
|
||||
|
@ -458,12 +492,15 @@ class SocketChannelImpl
|
|||
throws AsynchronousCloseException
|
||||
{
|
||||
if (blocking) {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
writerThread = 0;
|
||||
// notify any thread waiting in implCloseSelectableChannel
|
||||
if (state == ST_CLOSING) {
|
||||
stateLock.notifyAll();
|
||||
stateCondition.signalAll();
|
||||
}
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
// remove hook for Thread.interrupt
|
||||
end(completed);
|
||||
|
@ -480,12 +517,12 @@ class SocketChannelImpl
|
|||
int n = 0;
|
||||
try {
|
||||
beginWrite(blocking);
|
||||
n = IOUtil.write(fd, buf, -1, nd);
|
||||
if (blocking) {
|
||||
do {
|
||||
while (IOStatus.okayToRetry(n) && isOpen()) {
|
||||
park(Net.POLLOUT);
|
||||
n = IOUtil.write(fd, buf, -1, nd);
|
||||
} while (n == IOStatus.INTERRUPTED && isOpen());
|
||||
} else {
|
||||
n = IOUtil.write(fd, buf, -1, nd);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
endWrite(blocking, n > 0);
|
||||
|
@ -510,12 +547,12 @@ class SocketChannelImpl
|
|||
long n = 0;
|
||||
try {
|
||||
beginWrite(blocking);
|
||||
n = IOUtil.write(fd, srcs, offset, length, nd);
|
||||
if (blocking) {
|
||||
do {
|
||||
while (IOStatus.okayToRetry(n) && isOpen()) {
|
||||
park(Net.POLLOUT);
|
||||
n = IOUtil.write(fd, srcs, offset, length, nd);
|
||||
} while (n == IOStatus.INTERRUPTED && isOpen());
|
||||
} else {
|
||||
n = IOUtil.write(fd, srcs, offset, length, nd);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
endWrite(blocking, n > 0);
|
||||
|
@ -562,10 +599,7 @@ class SocketChannelImpl
|
|||
try {
|
||||
writeLock.lock();
|
||||
try {
|
||||
synchronized (stateLock) {
|
||||
ensureOpen();
|
||||
IOUtil.configureBlocking(fd, block);
|
||||
}
|
||||
lockedConfigureBlocking(block);
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
|
@ -574,12 +608,29 @@ class SocketChannelImpl
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the blocking mode while holding the readLock or writeLock.
|
||||
*/
|
||||
private void lockedConfigureBlocking(boolean block) throws IOException {
|
||||
assert readLock.isHeldByCurrentThread() || writeLock.isHeldByCurrentThread();
|
||||
stateLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
IOUtil.configureBlocking(fd, block);
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the local address, or null if not bound
|
||||
*/
|
||||
InetSocketAddress localAddress() {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
return localAddress;
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -587,8 +638,11 @@ class SocketChannelImpl
|
|||
* Returns the remote address, or null if not connected
|
||||
*/
|
||||
InetSocketAddress remoteAddress() {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
return remoteAddress;
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -598,7 +652,8 @@ class SocketChannelImpl
|
|||
try {
|
||||
writeLock.lock();
|
||||
try {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
if (state == ST_CONNECTIONPENDING)
|
||||
throw new ConnectionPendingException();
|
||||
|
@ -613,6 +668,8 @@ class SocketChannelImpl
|
|||
NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());
|
||||
Net.bind(fd, isa.getAddress(), isa.getPort());
|
||||
localAddress = Net.localAddress(fd);
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
|
@ -649,7 +706,8 @@ class SocketChannelImpl
|
|||
// set hook for Thread.interrupt
|
||||
begin();
|
||||
}
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
int state = this.state;
|
||||
if (state == ST_CONNECTED)
|
||||
|
@ -667,6 +725,8 @@ class SocketChannelImpl
|
|||
// record thread so it can be signalled if needed
|
||||
readerThread = NativeThread.current();
|
||||
}
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -683,43 +743,62 @@ class SocketChannelImpl
|
|||
endRead(blocking, completed);
|
||||
|
||||
if (completed) {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
if (state == ST_CONNECTIONPENDING) {
|
||||
localAddress = Net.localAddress(fd);
|
||||
state = ST_CONNECTED;
|
||||
}
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean connect(SocketAddress sa) throws IOException {
|
||||
/**
|
||||
* Checks the remote address to which this channel is to be connected.
|
||||
*/
|
||||
private InetSocketAddress checkRemote(SocketAddress sa) throws IOException {
|
||||
InetSocketAddress isa = Net.checkAddress(sa);
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null)
|
||||
if (sm != null) {
|
||||
sm.checkConnect(isa.getAddress().getHostAddress(), isa.getPort());
|
||||
}
|
||||
if (isa.getAddress().isAnyLocalAddress()) {
|
||||
return new InetSocketAddress(InetAddress.getLocalHost(), isa.getPort());
|
||||
} else {
|
||||
return isa;
|
||||
}
|
||||
}
|
||||
|
||||
InetAddress ia = isa.getAddress();
|
||||
if (ia.isAnyLocalAddress())
|
||||
ia = InetAddress.getLocalHost();
|
||||
|
||||
@Override
|
||||
public boolean connect(SocketAddress remote) throws IOException {
|
||||
InetSocketAddress isa = checkRemote(remote);
|
||||
try {
|
||||
readLock.lock();
|
||||
try {
|
||||
writeLock.lock();
|
||||
try {
|
||||
int n = 0;
|
||||
boolean blocking = isBlocking();
|
||||
boolean connected = false;
|
||||
try {
|
||||
beginConnect(blocking, isa);
|
||||
do {
|
||||
n = Net.connect(fd, ia, isa.getPort());
|
||||
} while (n == IOStatus.INTERRUPTED && isOpen());
|
||||
int n = Net.connect(fd, isa.getAddress(), isa.getPort());
|
||||
if (n > 0) {
|
||||
connected = true;
|
||||
} else if (blocking) {
|
||||
assert IOStatus.okayToRetry(n);
|
||||
boolean polled = false;
|
||||
while (!polled && isOpen()) {
|
||||
park(Net.POLLOUT);
|
||||
polled = Net.pollConnectNow(fd);
|
||||
}
|
||||
connected = polled && isOpen();
|
||||
}
|
||||
} finally {
|
||||
endConnect(blocking, (n > 0));
|
||||
endConnect(blocking, connected);
|
||||
}
|
||||
assert IOStatus.check(n);
|
||||
return n > 0;
|
||||
return connected;
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
|
@ -744,7 +823,8 @@ class SocketChannelImpl
|
|||
// set hook for Thread.interrupt
|
||||
begin();
|
||||
}
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
if (state != ST_CONNECTIONPENDING)
|
||||
throw new NoConnectionPendingException();
|
||||
|
@ -752,6 +832,8 @@ class SocketChannelImpl
|
|||
// record thread so it can be signalled if needed
|
||||
readerThread = NativeThread.current();
|
||||
}
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -768,11 +850,14 @@ class SocketChannelImpl
|
|||
endRead(blocking, completed);
|
||||
|
||||
if (completed) {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
if (state == ST_CONNECTIONPENDING) {
|
||||
localAddress = Net.localAddress(fd);
|
||||
state = ST_CONNECTED;
|
||||
}
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -792,13 +877,14 @@ class SocketChannelImpl
|
|||
boolean connected = false;
|
||||
try {
|
||||
beginFinishConnect(blocking);
|
||||
boolean polled = Net.pollConnectNow(fd);
|
||||
if (blocking) {
|
||||
do {
|
||||
connected = Net.pollConnect(fd, -1);
|
||||
} while (!connected && isOpen());
|
||||
} else {
|
||||
connected = Net.pollConnect(fd, 0);
|
||||
while (!polled && isOpen()) {
|
||||
park(Net.POLLOUT);
|
||||
polled = Net.pollConnectNow(fd);
|
||||
}
|
||||
}
|
||||
connected = polled && isOpen();
|
||||
} finally {
|
||||
endFinishConnect(blocking, connected);
|
||||
}
|
||||
|
@ -843,16 +929,20 @@ class SocketChannelImpl
|
|||
boolean interrupted = false;
|
||||
|
||||
// set state to ST_CLOSING
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
assert state < ST_CLOSING;
|
||||
blocking = isBlocking();
|
||||
connected = (state == ST_CONNECTED);
|
||||
state = ST_CLOSING;
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
|
||||
// wait for any outstanding I/O operations to complete
|
||||
if (blocking) {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
assert state == ST_CLOSING;
|
||||
long reader = readerThread;
|
||||
long writer = writerThread;
|
||||
|
@ -868,12 +958,14 @@ class SocketChannelImpl
|
|||
// wait for blocking I/O operations to end
|
||||
while (readerThread != 0 || writerThread != 0) {
|
||||
try {
|
||||
stateLock.wait();
|
||||
stateCondition.await();
|
||||
} catch (InterruptedException e) {
|
||||
interrupted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
} else {
|
||||
// non-blocking mode: wait for read/write to complete
|
||||
|
@ -887,7 +979,8 @@ class SocketChannelImpl
|
|||
}
|
||||
|
||||
// set state to ST_KILLPENDING
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
assert state == ST_CLOSING;
|
||||
// if connected and the channel is registered with a Selector then
|
||||
// shutdown the output if possible so that the peer reads EOF. If
|
||||
|
@ -908,6 +1001,8 @@ class SocketChannelImpl
|
|||
} catch (IOException ignore) { }
|
||||
}
|
||||
state = ST_KILLPENDING;
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
|
||||
// close socket if not registered with Selector
|
||||
|
@ -921,17 +1016,21 @@ class SocketChannelImpl
|
|||
|
||||
@Override
|
||||
public void kill() throws IOException {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
if (state == ST_KILLPENDING) {
|
||||
state = ST_KILLED;
|
||||
nd.close(fd);
|
||||
}
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketChannel shutdownInput() throws IOException {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
if (!isConnected())
|
||||
throw new NotYetConnectedException();
|
||||
|
@ -943,12 +1042,15 @@ class SocketChannelImpl
|
|||
isInputClosed = true;
|
||||
}
|
||||
return this;
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketChannel shutdownOutput() throws IOException {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
if (!isConnected())
|
||||
throw new NotYetConnectedException();
|
||||
|
@ -960,6 +1062,8 @@ class SocketChannelImpl
|
|||
isOutputClosed = true;
|
||||
}
|
||||
return this;
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -972,58 +1076,223 @@ class SocketChannelImpl
|
|||
}
|
||||
|
||||
/**
|
||||
* Poll this channel's socket for reading up to the given timeout.
|
||||
* @return {@code true} if the socket is polled
|
||||
* Waits for a connection attempt to finish with a timeout
|
||||
* @throws SocketTimeoutException if the connect timeout elapses
|
||||
*/
|
||||
boolean pollRead(long timeout) throws IOException {
|
||||
boolean blocking = isBlocking();
|
||||
assert Thread.holdsLock(blockingLock()) && blocking;
|
||||
private boolean finishTimedConnect(long nanos) throws IOException {
|
||||
long startNanos = System.nanoTime();
|
||||
boolean polled = Net.pollConnectNow(fd);
|
||||
while (!polled && isOpen()) {
|
||||
long remainingNanos = nanos - (System.nanoTime() - startNanos);
|
||||
if (remainingNanos <= 0) {
|
||||
throw new SocketTimeoutException("Connect timed out");
|
||||
}
|
||||
park(Net.POLLOUT, remainingNanos);
|
||||
polled = Net.pollConnectNow(fd);
|
||||
}
|
||||
return polled && isOpen();
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to establish a connection to the given socket address with a
|
||||
* timeout. Closes the socket if connection cannot be established.
|
||||
*
|
||||
* @apiNote This method is for use by the socket adaptor.
|
||||
*
|
||||
* @throws IllegalBlockingModeException if the channel is non-blocking
|
||||
* @throws SocketTimeoutException if the read timeout elapses
|
||||
*/
|
||||
void blockingConnect(SocketAddress remote, long nanos) throws IOException {
|
||||
InetSocketAddress isa = checkRemote(remote);
|
||||
try {
|
||||
readLock.lock();
|
||||
try {
|
||||
writeLock.lock();
|
||||
try {
|
||||
if (!isBlocking())
|
||||
throw new IllegalBlockingModeException();
|
||||
boolean connected = false;
|
||||
try {
|
||||
beginConnect(true, isa);
|
||||
// change socket to non-blocking
|
||||
lockedConfigureBlocking(false);
|
||||
try {
|
||||
int n = Net.connect(fd, isa.getAddress(), isa.getPort());
|
||||
connected = (n > 0) ? true : finishTimedConnect(nanos);
|
||||
} finally {
|
||||
// restore socket to blocking mode
|
||||
lockedConfigureBlocking(true);
|
||||
}
|
||||
} finally {
|
||||
endConnect(true, connected);
|
||||
}
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
// connect failed, close the channel
|
||||
close();
|
||||
throw SocketExceptions.of(ioe, isa);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to read bytes from the socket into the given byte array.
|
||||
*/
|
||||
private int tryRead(byte[] b, int off, int len) throws IOException {
|
||||
ByteBuffer dst = Util.getTemporaryDirectBuffer(len);
|
||||
assert dst.position() == 0;
|
||||
try {
|
||||
int n = nd.read(fd, ((DirectBuffer)dst).address(), len);
|
||||
if (n > 0) {
|
||||
dst.get(b, off, n);
|
||||
}
|
||||
return n;
|
||||
} finally{
|
||||
Util.offerFirstTemporaryDirectBuffer(dst);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads bytes from the socket into the given byte array with a timeout.
|
||||
* @throws SocketTimeoutException if the read timeout elapses
|
||||
*/
|
||||
private int timedRead(byte[] b, int off, int len, long nanos) throws IOException {
|
||||
long startNanos = System.nanoTime();
|
||||
int n = tryRead(b, off, len);
|
||||
while (n == IOStatus.UNAVAILABLE && isOpen()) {
|
||||
long remainingNanos = nanos - (System.nanoTime() - startNanos);
|
||||
if (remainingNanos <= 0) {
|
||||
throw new SocketTimeoutException("Read timed out");
|
||||
}
|
||||
park(Net.POLLIN, remainingNanos);
|
||||
n = tryRead(b, off, len);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads bytes from the socket into the given byte array.
|
||||
*
|
||||
* @apiNote This method is for use by the socket adaptor.
|
||||
*
|
||||
* @throws IllegalBlockingModeException if the channel is non-blocking
|
||||
* @throws SocketTimeoutException if the read timeout elapses
|
||||
*/
|
||||
int blockingRead(byte[] b, int off, int len, long nanos) throws IOException {
|
||||
Objects.checkFromIndexSize(off, len, b.length);
|
||||
if (len == 0) {
|
||||
// nothing to do
|
||||
return 0;
|
||||
}
|
||||
|
||||
readLock.lock();
|
||||
try {
|
||||
boolean polled = false;
|
||||
// check that channel is configured blocking
|
||||
if (!isBlocking())
|
||||
throw new IllegalBlockingModeException();
|
||||
|
||||
int n = 0;
|
||||
try {
|
||||
beginRead(blocking);
|
||||
int events = Net.poll(fd, Net.POLLIN, timeout);
|
||||
polled = (events != 0);
|
||||
beginRead(true);
|
||||
|
||||
// check if connection has been reset
|
||||
if (connectionReset)
|
||||
throwConnectionReset();
|
||||
|
||||
// check if input is shutdown
|
||||
if (isInputClosed)
|
||||
return IOStatus.EOF;
|
||||
|
||||
if (nanos > 0) {
|
||||
// change socket to non-blocking
|
||||
lockedConfigureBlocking(false);
|
||||
try {
|
||||
n = timedRead(b, off, len, nanos);
|
||||
} finally {
|
||||
// restore socket to blocking mode
|
||||
lockedConfigureBlocking(true);
|
||||
}
|
||||
} else {
|
||||
// read, no timeout
|
||||
n = tryRead(b, off, len);
|
||||
while (IOStatus.okayToRetry(n) && isOpen()) {
|
||||
park(Net.POLLIN);
|
||||
n = tryRead(b, off, len);
|
||||
}
|
||||
}
|
||||
} catch (ConnectionResetException e) {
|
||||
connectionReset = true;
|
||||
throwConnectionReset();
|
||||
} finally {
|
||||
endRead(blocking, polled);
|
||||
endRead(true, n > 0);
|
||||
if (n <= 0 && isInputClosed)
|
||||
return IOStatus.EOF;
|
||||
}
|
||||
return polled;
|
||||
assert n > 0 || n == -1;
|
||||
return n;
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Poll this channel's socket for a connection, up to the given timeout.
|
||||
* @return {@code true} if the socket is polled
|
||||
* Attempts to write a sequence of bytes to the socket from the given
|
||||
* byte array.
|
||||
*/
|
||||
boolean pollConnected(long timeout) throws IOException {
|
||||
boolean blocking = isBlocking();
|
||||
assert Thread.holdsLock(blockingLock()) && blocking;
|
||||
|
||||
readLock.lock();
|
||||
private int tryWrite(byte[] b, int off, int len) throws IOException {
|
||||
ByteBuffer src = Util.getTemporaryDirectBuffer(len);
|
||||
assert src.position() == 0;
|
||||
try {
|
||||
writeLock.lock();
|
||||
src.put(b, off, len);
|
||||
return nd.write(fd, ((DirectBuffer)src).address(), len);
|
||||
} finally {
|
||||
Util.offerFirstTemporaryDirectBuffer(src);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a sequence of bytes to the socket from the given byte array.
|
||||
*
|
||||
* @apiNote This method is for use by the socket adaptor.
|
||||
*/
|
||||
void blockingWriteFully(byte[] b, int off, int len) throws IOException {
|
||||
Objects.checkFromIndexSize(off, len, b.length);
|
||||
if (len == 0) {
|
||||
// nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
writeLock.lock();
|
||||
try {
|
||||
// check that channel is configured blocking
|
||||
if (!isBlocking())
|
||||
throw new IllegalBlockingModeException();
|
||||
|
||||
// loop until all bytes have been written
|
||||
int pos = off;
|
||||
int end = off + len;
|
||||
beginWrite(true);
|
||||
try {
|
||||
boolean polled = false;
|
||||
try {
|
||||
beginFinishConnect(blocking);
|
||||
int events = Net.poll(fd, Net.POLLCONN, timeout);
|
||||
polled = (events != 0);
|
||||
} finally {
|
||||
// invoke endFinishConnect with completed = false so that
|
||||
// the state is not changed to ST_CONNECTED. The socket
|
||||
// adaptor will use finishConnect to finish.
|
||||
endFinishConnect(blocking, /*completed*/false);
|
||||
while (pos < end && isOpen()) {
|
||||
int size = end - pos;
|
||||
int n = tryWrite(b, pos, size);
|
||||
while (IOStatus.okayToRetry(n) && isOpen()) {
|
||||
park(Net.POLLOUT);
|
||||
n = tryWrite(b, pos, size);
|
||||
}
|
||||
if (n > 0) {
|
||||
pos += n;
|
||||
}
|
||||
}
|
||||
return polled;
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
endWrite(true, pos >= end);
|
||||
}
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1031,13 +1300,16 @@ class SocketChannelImpl
|
|||
* Return the number of bytes in the socket input buffer.
|
||||
*/
|
||||
int available() throws IOException {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
ensureOpenAndConnected();
|
||||
if (isInputClosed) {
|
||||
return 0;
|
||||
} else {
|
||||
return Net.available(fd);
|
||||
}
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1117,7 +1389,8 @@ class SocketChannelImpl
|
|||
if (!isOpen())
|
||||
sb.append("closed");
|
||||
else {
|
||||
synchronized (stateLock) {
|
||||
stateLock.lock();
|
||||
try {
|
||||
switch (state) {
|
||||
case ST_UNCONNECTED:
|
||||
sb.append("unconnected");
|
||||
|
@ -1142,6 +1415,8 @@ class SocketChannelImpl
|
|||
sb.append(" remote=");
|
||||
sb.append(remoteAddress().toString());
|
||||
}
|
||||
} finally {
|
||||
stateLock.unlock();
|
||||
}
|
||||
}
|
||||
sb.append(']');
|
||||
|
|
Binary file not shown.
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2004, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2004, 2019, 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
|
||||
|
@ -109,7 +109,9 @@ enum {
|
|||
JVM_CONSTANT_MethodType = 16, // JSR 292
|
||||
JVM_CONSTANT_Dynamic = 17,
|
||||
JVM_CONSTANT_InvokeDynamic = 18,
|
||||
JVM_CONSTANT_ExternalMax = 18
|
||||
JVM_CONSTANT_Module = 19,
|
||||
JVM_CONSTANT_Package = 20,
|
||||
JVM_CONSTANT_ExternalMax = 20
|
||||
};
|
||||
|
||||
/* JVM_CONSTANT_MethodHandle subtypes */
|
||||
|
|
|
@ -27,12 +27,19 @@
|
|||
|
||||
#include "java_net_InetAddress.h"
|
||||
|
||||
int IPv4_supported();
|
||||
int IPv6_supported();
|
||||
int reuseport_supported();
|
||||
|
||||
static int IPv4_available;
|
||||
static int IPv6_available;
|
||||
static int REUSEPORT_available;
|
||||
|
||||
JNIEXPORT jint JNICALL ipv4_available()
|
||||
{
|
||||
return IPv4_available;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL ipv6_available()
|
||||
{
|
||||
return IPv6_available;
|
||||
|
@ -68,6 +75,7 @@ DEF_JNI_OnLoad(JavaVM *vm, void *reserved)
|
|||
* check now whether we have IPv6 on this platform and if the
|
||||
* supporting socket APIs are available
|
||||
*/
|
||||
IPv4_available = IPv4_supported();
|
||||
IPv6_available = IPv6_supported() & (!preferIPv4Stack);
|
||||
|
||||
/* check if SO_REUSEPORT is supported on this platform */
|
||||
|
|
|
@ -126,6 +126,7 @@ void NET_ThrowCurrent(JNIEnv *env, char *msg);
|
|||
|
||||
jfieldID NET_GetFileDescriptorID(JNIEnv *env);
|
||||
|
||||
JNIEXPORT jint JNICALL ipv4_available();
|
||||
JNIEXPORT jint JNICALL ipv6_available();
|
||||
|
||||
JNIEXPORT jint JNICALL reuseport_available();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue