diff options
Diffstat (limited to 'luni/src/main/java')
328 files changed, 13954 insertions, 4098 deletions
diff --git a/luni/src/main/java/java/beans/PropertyChangeSupport.java b/luni/src/main/java/java/beans/PropertyChangeSupport.java index 04f8155..1db12b7 100644 --- a/luni/src/main/java/java/beans/PropertyChangeSupport.java +++ b/luni/src/main/java/java/beans/PropertyChangeSupport.java @@ -66,7 +66,7 @@ public class PropertyChangeSupport implements Serializable { */ public PropertyChangeSupport(Object sourceBean) { if (sourceBean == null) { - throw new NullPointerException(); + throw new NullPointerException("sourceBean == null"); } this.sourceBean = sourceBean; } diff --git a/luni/src/main/java/java/io/BufferedWriter.java b/luni/src/main/java/java/io/BufferedWriter.java index e4cbe7c..55ae121 100644 --- a/luni/src/main/java/java/io/BufferedWriter.java +++ b/luni/src/main/java/java/io/BufferedWriter.java @@ -163,33 +163,33 @@ public class BufferedWriter extends Writer { /** * Writes {@code count} characters starting at {@code offset} in - * {@code cbuf} to this writer. If {@code count} is greater than this + * {@code buffer} to this writer. If {@code count} is greater than this * writer's buffer, then the buffer is flushed and the characters are * written directly to the target writer. * - * @param cbuf + * @param buffer * the array containing characters to write. * @param offset - * the start position in {@code cbuf} for retrieving characters. + * the start position in {@code buffer} for retrieving characters. * @param count * the maximum number of characters to write. * @throws IndexOutOfBoundsException * if {@code offset < 0} or {@code count < 0}, or if * {@code offset + count} is greater than the size of - * {@code cbuf}. + * {@code buffer}. * @throws IOException * if this writer is closed or another I/O error occurs. */ @Override - public void write(char[] cbuf, int offset, int count) throws IOException { + public void write(char[] buffer, int offset, int count) throws IOException { synchronized (lock) { checkNotClosed(); - if (cbuf == null) { + if (buffer == null) { throw new NullPointerException("buffer == null"); } - Arrays.checkOffsetAndCount(cbuf.length, offset, count); + Arrays.checkOffsetAndCount(buffer.length, offset, count); if (pos == 0 && count >= this.buf.length) { - out.write(cbuf, offset, count); + out.write(buffer, offset, count); return; } int available = this.buf.length - pos; @@ -197,7 +197,7 @@ public class BufferedWriter extends Writer { available = count; } if (available > 0) { - System.arraycopy(cbuf, offset, this.buf, pos, available); + System.arraycopy(buffer, offset, this.buf, pos, available); pos += available; } if (pos == this.buf.length) { @@ -207,11 +207,11 @@ public class BufferedWriter extends Writer { offset += available; available = count - available; if (available >= this.buf.length) { - out.write(cbuf, offset, available); + out.write(buffer, offset, available); return; } - System.arraycopy(cbuf, offset, this.buf, pos, available); + System.arraycopy(buffer, offset, this.buf, pos, available); pos += available; } } diff --git a/luni/src/main/java/java/io/ByteArrayOutputStream.java b/luni/src/main/java/java/io/ByteArrayOutputStream.java index 3ab2c20..ff9c7df 100644 --- a/luni/src/main/java/java/io/ByteArrayOutputStream.java +++ b/luni/src/main/java/java/io/ByteArrayOutputStream.java @@ -162,17 +162,17 @@ public class ByteArrayOutputStream extends OutputStream { /** * Returns the contents of this ByteArrayOutputStream as a string converted - * according to the encoding declared in {@code enc}. + * according to the encoding declared in {@code charsetName}. * - * @param enc + * @param charsetName * a string representing the encoding to use when translating * this stream to a string. * @return this stream's current contents as an encoded string. * @throws UnsupportedEncodingException * if the provided encoding is not supported. */ - public String toString(String enc) throws UnsupportedEncodingException { - return new String(buf, 0, count, enc); + public String toString(String charsetName) throws UnsupportedEncodingException { + return new String(buf, 0, count, charsetName); } /** diff --git a/luni/src/main/java/java/io/File.java b/luni/src/main/java/java/io/File.java index 968f021..ec87fed 100644 --- a/luni/src/main/java/java/io/File.java +++ b/luni/src/main/java/java/io/File.java @@ -147,7 +147,7 @@ public class File implements Serializable, Comparable<File> { */ public File(String dirPath, String name) { if (name == null) { - throw new NullPointerException(); + throw new NullPointerException("name == null"); } if (dirPath == null || dirPath.isEmpty()) { this.path = fixSlashes(name); @@ -855,57 +855,65 @@ public class File implements Serializable, Comparable<File> { } /** - * Creates the directory named by the trailing filename of this file. Does - * not create the complete path required to create this directory. + * Creates the directory named by this file, assuming its parents exist. + * Use {@link #mkdirs} if you also want to create missing parents. * * <p>Note that this method does <i>not</i> throw {@code IOException} on failure. - * Callers must check the return value. + * Callers must check the return value. Note also that this method returns + * false if the directory already existed. If you want to know whether the + * directory exists on return, either use {@code (f.mkdir() || f.isDirectory())} + * or simply ignore the return value from this method and simply call {@link #isDirectory}. * - * @return {@code true} if the directory has been created, {@code false} - * otherwise. - * @see #mkdirs + * @return {@code true} if the directory was created, + * {@code false} on failure or if the directory already existed. */ public boolean mkdir() { try { - // On Android, we don't want default permissions to allow global access. - Libcore.os.mkdir(path, S_IRWXU); + mkdirErrno(); return true; } catch (ErrnoException errnoException) { return false; } } + private void mkdirErrno() throws ErrnoException { + // On Android, we don't want default permissions to allow global access. + Libcore.os.mkdir(path, S_IRWXU); + } + /** - * Creates the directory named by the trailing filename of this file, - * including the complete directory path required to create this directory. + * Creates the directory named by this file, creating missing parent + * directories if necessary. + * Use {@link #mkdir} if you don't want to create missing parents. * * <p>Note that this method does <i>not</i> throw {@code IOException} on failure. - * Callers must check the return value. + * Callers must check the return value. Note also that this method returns + * false if the directory already existed. If you want to know whether the + * directory exists on return, either use {@code (f.mkdirs() || f.isDirectory())} + * or simply ignore the return value from this method and simply call {@link #isDirectory}. * - * @return {@code true} if the necessary directories have been created, - * {@code false} if the target directory already exists or one of - * the directories can not be created. - * @see #mkdir + * @return {@code true} if the directory was created, + * {@code false} on failure or if the directory already existed. */ public boolean mkdirs() { - /* If the terminal directory already exists, answer false */ - if (exists()) { - return false; - } + return mkdirs(false); + } - /* If the receiver can be created, answer true */ - if (mkdir()) { + private boolean mkdirs(boolean resultIfExists) { + try { + // Try to create the directory directly. + mkdirErrno(); return true; - } - - String parentDir = getParent(); - /* If there is no parent and we were not created, answer false */ - if (parentDir == null) { + } catch (ErrnoException errnoException) { + if (errnoException.errno == ENOENT) { + // If the parent was missing, try to create it and then try again. + File parent = getParentFile(); + return parent != null && parent.mkdirs(true) && mkdir(); + } else if (errnoException.errno == EEXIST) { + return resultIfExists; + } return false; } - - /* Otherwise, try to create a parent directory and then this directory */ - return (new File(parentDir).mkdirs() && mkdir()); } /** diff --git a/luni/src/main/java/java/io/FileDescriptor.java b/luni/src/main/java/java/io/FileDescriptor.java index f04ae2c..e4eb06c 100644 --- a/luni/src/main/java/java/io/FileDescriptor.java +++ b/luni/src/main/java/java/io/FileDescriptor.java @@ -68,7 +68,11 @@ public final class FileDescriptor { */ public void sync() throws SyncFailedException { try { - Libcore.os.fsync(this); + if (Libcore.os.isatty(this)) { + Libcore.os.tcdrain(this); + } else { + Libcore.os.fsync(this); + } } catch (ErrnoException errnoException) { SyncFailedException sfe = new SyncFailedException(errnoException.getMessage()); sfe.initCause(errnoException); diff --git a/luni/src/main/java/java/io/InputStreamReader.java b/luni/src/main/java/java/io/InputStreamReader.java index 59be9ed..d3650dc 100644 --- a/luni/src/main/java/java/io/InputStreamReader.java +++ b/luni/src/main/java/java/io/InputStreamReader.java @@ -62,32 +62,32 @@ public class InputStreamReader extends Reader { /** * Constructs a new InputStreamReader on the InputStream {@code in}. The * character converter that is used to decode bytes into characters is - * identified by name by {@code enc}. If the encoding cannot be found, an + * identified by name by {@code charsetName}. If the encoding cannot be found, an * UnsupportedEncodingException error is thrown. * * @param in * the InputStream from which to read characters. - * @param enc + * @param charsetName * identifies the character converter to use. * @throws NullPointerException - * if {@code enc} is {@code null}. + * if {@code charsetName} is {@code null}. * @throws UnsupportedEncodingException - * if the encoding specified by {@code enc} cannot be found. + * if the encoding specified by {@code charsetName} cannot be found. */ - public InputStreamReader(InputStream in, final String enc) + public InputStreamReader(InputStream in, final String charsetName) throws UnsupportedEncodingException { super(in); - if (enc == null) { - throw new NullPointerException(); + if (charsetName == null) { + throw new NullPointerException("charsetName == null"); } this.in = in; try { - decoder = Charset.forName(enc).newDecoder().onMalformedInput( + decoder = Charset.forName(charsetName).newDecoder().onMalformedInput( CodingErrorAction.REPLACE).onUnmappableCharacter( CodingErrorAction.REPLACE); } catch (IllegalArgumentException e) { throw (UnsupportedEncodingException) - new UnsupportedEncodingException(enc).initCause(e); + new UnsupportedEncodingException(charsetName).initCause(e); } bytes.limit(0); } diff --git a/luni/src/main/java/java/io/ObjectInputStream.java b/luni/src/main/java/java/io/ObjectInputStream.java index 4541f1b..0476901 100644 --- a/luni/src/main/java/java/io/ObjectInputStream.java +++ b/luni/src/main/java/java/io/ObjectInputStream.java @@ -23,8 +23,8 @@ import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.lang.reflect.Proxy; -import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -1089,8 +1089,11 @@ public class ObjectInputStream extends InputStream implements ObjectInput, Objec for (ObjectStreamField fieldDesc : fields) { Field field = classDesc.getReflectionField(fieldDesc); - // We may not have been able to find the field, but we still need to read the value - // and do the other checking, so there's no null check on 'field' here. + if (field != null && Modifier.isTransient(field.getModifiers())) { + field = null; // No setting transient fields! (http://b/4471249) + } + // We may not have been able to find the field, or it may be transient, but we still + // need to read the value and do the other checking... try { Class<?> type = fieldDesc.getTypeInternal(); if (type == byte.class) { @@ -2341,7 +2344,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, Objec public int skipBytes(int length) throws IOException { // To be used with available. Ok to call if reading primitive buffer if (input == null) { - throw new NullPointerException(); + throw new NullPointerException("source stream is null"); } int offset = 0; diff --git a/luni/src/main/java/java/io/ObjectStreamClass.java b/luni/src/main/java/java/io/ObjectStreamClass.java index e87fcd4..a28489a 100644 --- a/luni/src/main/java/java/io/ObjectStreamClass.java +++ b/luni/src/main/java/java/io/ObjectStreamClass.java @@ -481,16 +481,14 @@ public class ObjectStreamClass implements Serializable { Field field = fields[i]; int modifiers = field.getModifiers() & FIELD_MODIFIERS_MASK; - boolean skip = Modifier.isPrivate(modifiers) - && (Modifier.isTransient(modifiers) || Modifier - .isStatic(modifiers)); + boolean skip = Modifier.isPrivate(modifiers) && + (Modifier.isTransient(modifiers) || Modifier.isStatic(modifiers)); if (!skip) { // write name, modifier & "descriptor" of all but private // static and private transient output.writeUTF(field.getName()); output.writeInt(modifiers); - output - .writeUTF(descriptorForFieldSignature(getFieldSignature(field))); + output.writeUTF(descriptorForFieldSignature(getFieldSignature(field))); } } diff --git a/luni/src/main/java/java/io/ObjectStreamField.java b/luni/src/main/java/java/io/ObjectStreamField.java index db450e0..78a6903 100644 --- a/luni/src/main/java/java/io/ObjectStreamField.java +++ b/luni/src/main/java/java/io/ObjectStreamField.java @@ -58,8 +58,10 @@ public class ObjectStreamField implements Comparable<Object> { * if {@code name} or {@code cl} is {@code null}. */ public ObjectStreamField(String name, Class<?> cl) { - if (name == null || cl == null) { - throw new NullPointerException(); + if (name == null) { + throw new NullPointerException("name == null"); + } else if (cl == null) { + throw new NullPointerException("cl == null"); } this.name = name; this.type = new WeakReference<Class<?>>(cl); @@ -81,8 +83,10 @@ public class ObjectStreamField implements Comparable<Object> { * @see ObjectOutputStream#writeUnshared(Object) */ public ObjectStreamField(String name, Class<?> cl, boolean unshared) { - if (name == null || cl == null) { - throw new NullPointerException(); + if (name == null) { + throw new NullPointerException("name == null"); + } else if (cl == null) { + throw new NullPointerException("cl == null"); } this.name = name; this.type = (cl.getClassLoader() == null) ? cl : new WeakReference<Class<?>>(cl); @@ -100,7 +104,7 @@ public class ObjectStreamField implements Comparable<Object> { */ ObjectStreamField(String signature, String name) { if (name == null) { - throw new NullPointerException(); + throw new NullPointerException("name == null"); } this.name = name; this.typeString = signature.replace('.', '/').intern(); diff --git a/luni/src/main/java/java/io/OutputStreamWriter.java b/luni/src/main/java/java/io/OutputStreamWriter.java index 86b62fc..5dffdfe 100644 --- a/luni/src/main/java/java/io/OutputStreamWriter.java +++ b/luni/src/main/java/java/io/OutputStreamWriter.java @@ -57,30 +57,30 @@ public class OutputStreamWriter extends Writer { /** * Constructs a new OutputStreamWriter using {@code out} as the target - * stream to write converted characters to and {@code enc} as the character + * stream to write converted characters to and {@code charsetName} as the character * encoding. If the encoding cannot be found, an * UnsupportedEncodingException error is thrown. * * @param out * the target stream to write converted bytes to. - * @param enc + * @param charsetName * the string describing the desired character encoding. * @throws NullPointerException - * if {@code enc} is {@code null}. + * if {@code charsetName} is {@code null}. * @throws UnsupportedEncodingException - * if the encoding specified by {@code enc} cannot be found. + * if the encoding specified by {@code charsetName} cannot be found. */ - public OutputStreamWriter(OutputStream out, final String enc) + public OutputStreamWriter(OutputStream out, final String charsetName) throws UnsupportedEncodingException { super(out); - if (enc == null) { - throw new NullPointerException(); + if (charsetName == null) { + throw new NullPointerException("charsetName == null"); } this.out = out; try { - encoder = Charset.forName(enc).newEncoder(); + encoder = Charset.forName(charsetName).newEncoder(); } catch (Exception e) { - throw new UnsupportedEncodingException(enc); + throw new UnsupportedEncodingException(charsetName); } encoder.onMalformedInput(CodingErrorAction.REPLACE); encoder.onUnmappableCharacter(CodingErrorAction.REPLACE); @@ -106,19 +106,19 @@ public class OutputStreamWriter extends Writer { /** * Constructs a new OutputStreamWriter using {@code out} as the target - * stream to write converted characters to and {@code enc} as the character + * stream to write converted characters to and {@code charsetEncoder} as the character * encoder. * * @param out * the target stream to write converted bytes to. - * @param enc + * @param charsetEncoder * the character encoder used for character conversion. */ - public OutputStreamWriter(OutputStream out, CharsetEncoder enc) { + public OutputStreamWriter(OutputStream out, CharsetEncoder charsetEncoder) { super(out); - enc.charset(); + charsetEncoder.charset(); this.out = out; - encoder = enc; + encoder = charsetEncoder; } /** diff --git a/luni/src/main/java/java/io/PipedOutputStream.java b/luni/src/main/java/java/io/PipedOutputStream.java index a674bb3..1b139e9 100644 --- a/luni/src/main/java/java/io/PipedOutputStream.java +++ b/luni/src/main/java/java/io/PipedOutputStream.java @@ -81,7 +81,7 @@ public class PipedOutputStream extends OutputStream { */ public void connect(PipedInputStream stream) throws IOException { if (stream == null) { - throw new NullPointerException(); + throw new NullPointerException("stream == null"); } synchronized (stream) { if (this.target != null) { diff --git a/luni/src/main/java/java/io/PipedWriter.java b/luni/src/main/java/java/io/PipedWriter.java index ece899a..ad8974b 100644 --- a/luni/src/main/java/java/io/PipedWriter.java +++ b/luni/src/main/java/java/io/PipedWriter.java @@ -85,7 +85,7 @@ public class PipedWriter extends Writer { */ public void connect(PipedReader reader) throws IOException { if (reader == null) { - throw new NullPointerException(); + throw new NullPointerException("reader == null"); } synchronized (reader) { if (this.destination != null) { diff --git a/luni/src/main/java/java/io/PrintStream.java b/luni/src/main/java/java/io/PrintStream.java index ba48b04..18f2310 100644 --- a/luni/src/main/java/java/io/PrintStream.java +++ b/luni/src/main/java/java/io/PrintStream.java @@ -59,7 +59,7 @@ public class PrintStream extends FilterOutputStream implements Appendable, Close public PrintStream(OutputStream out) { super(out); if (out == null) { - throw new NullPointerException(); + throw new NullPointerException("out == null"); } } @@ -80,14 +80,14 @@ public class PrintStream extends FilterOutputStream implements Appendable, Close public PrintStream(OutputStream out, boolean autoFlush) { super(out); if (out == null) { - throw new NullPointerException(); + throw new NullPointerException("out == null"); } this.autoFlush = autoFlush; } /** * Constructs a new {@code PrintStream} with {@code out} as its target - * stream and using the character encoding {@code enc} while writing. The + * stream and using the character encoding {@code charsetName} while writing. The * parameter {@code autoFlush} determines if the print stream automatically * flushes its contents to the target stream when a newline is encountered. * @@ -96,28 +96,30 @@ public class PrintStream extends FilterOutputStream implements Appendable, Close * @param autoFlush * indicates whether or not to flush contents upon encountering a * newline sequence. - * @param enc + * @param charsetName * the non-null string describing the desired character encoding. * @throws NullPointerException - * if {@code out} or {@code enc} are {@code null}. + * if {@code out} or {@code charsetName} are {@code null}. * @throws UnsupportedEncodingException - * if the encoding specified by {@code enc} is not supported. + * if the encoding specified by {@code charsetName} is not supported. */ - public PrintStream(OutputStream out, boolean autoFlush, String enc) + public PrintStream(OutputStream out, boolean autoFlush, String charsetName) throws UnsupportedEncodingException { super(out); - if (out == null || enc == null) { - throw new NullPointerException(); + if (out == null) { + throw new NullPointerException("out == null"); + } else if (charsetName == null) { + throw new NullPointerException("charsetName == null"); } this.autoFlush = autoFlush; try { - if (!Charset.isSupported(enc)) { - throw new UnsupportedEncodingException(enc); + if (!Charset.isSupported(charsetName)) { + throw new UnsupportedEncodingException(charsetName); } } catch (IllegalCharsetNameException e) { - throw new UnsupportedEncodingException(enc); + throw new UnsupportedEncodingException(charsetName); } - encoding = enc; + encoding = charsetName; } /** @@ -136,30 +138,30 @@ public class PrintStream extends FilterOutputStream implements Appendable, Close /** * Constructs a new {@code PrintStream} with {@code file} as its target. The - * character set named {@code csn} is used for character encoding. + * character set named {@code charsetName} is used for character encoding. * * @param file * the target file. If the file already exists, its contents are * removed, otherwise a new file is created. - * @param csn + * @param charsetName * the name of the character set used for character encoding. * @throws FileNotFoundException * if an error occurs while opening or creating the target file. * @throws NullPointerException - * if {@code csn} is {@code null}. + * if {@code charsetName} is {@code null}. * @throws UnsupportedEncodingException - * if the encoding specified by {@code csn} is not supported. + * if the encoding specified by {@code charsetName} is not supported. */ - public PrintStream(File file, String csn) throws FileNotFoundException, + public PrintStream(File file, String charsetName) throws FileNotFoundException, UnsupportedEncodingException { super(new FileOutputStream(file)); - if (csn == null) { - throw new NullPointerException(); + if (charsetName == null) { + throw new NullPointerException("charsetName == null"); } - if (!Charset.isSupported(csn)) { - throw new UnsupportedEncodingException(csn); + if (!Charset.isSupported(charsetName)) { + throw new UnsupportedEncodingException(charsetName); } - encoding = csn; + encoding = charsetName; } /** @@ -179,24 +181,24 @@ public class PrintStream extends FilterOutputStream implements Appendable, Close /** * Constructs a new {@code PrintStream} with the file identified by - * {@code fileName} as its target. The character set named {@code csn} is + * {@code fileName} as its target. The character set named {@code charsetName} is * used for character encoding. * * @param fileName * the target file's name. If the file already exists, its * contents are removed, otherwise a new file is created. - * @param csn + * @param charsetName * the name of the character set used for character encoding. * @throws FileNotFoundException * if an error occurs while opening or creating the target file. * @throws NullPointerException - * if {@code csn} is {@code null}. + * if {@code charsetName} is {@code null}. * @throws UnsupportedEncodingException - * if the encoding specified by {@code csn} is not supported. + * if the encoding specified by {@code charsetName} is not supported. */ - public PrintStream(String fileName, String csn) + public PrintStream(String fileName, String charsetName) throws FileNotFoundException, UnsupportedEncodingException { - this(new File(fileName), csn); + this(new File(fileName), charsetName); } /** diff --git a/luni/src/main/java/java/io/Reader.java b/luni/src/main/java/java/io/Reader.java index 310a57c..e947d08 100644 --- a/luni/src/main/java/java/io/Reader.java +++ b/luni/src/main/java/java/io/Reader.java @@ -61,7 +61,7 @@ public abstract class Reader implements Readable, Closeable { */ protected Reader(Object lock) { if (lock == null) { - throw new NullPointerException(); + throw new NullPointerException("lock == null"); } this.lock = lock; } diff --git a/luni/src/main/java/java/io/SequenceInputStream.java b/luni/src/main/java/java/io/SequenceInputStream.java index 9ae1901..8333834 100644 --- a/luni/src/main/java/java/io/SequenceInputStream.java +++ b/luni/src/main/java/java/io/SequenceInputStream.java @@ -50,7 +50,7 @@ public class SequenceInputStream extends InputStream { */ public SequenceInputStream(InputStream s1, InputStream s2) { if (s1 == null) { - throw new NullPointerException(); + throw new NullPointerException("s1 == null"); } Vector<InputStream> inVector = new Vector<InputStream>(1); inVector.addElement(s2); @@ -73,7 +73,7 @@ public class SequenceInputStream extends InputStream { if (e.hasMoreElements()) { in = e.nextElement(); if (in == null) { - throw new NullPointerException(); + throw new NullPointerException("element is null"); } } } @@ -112,7 +112,7 @@ public class SequenceInputStream extends InputStream { if (e.hasMoreElements()) { in = e.nextElement(); if (in == null) { - throw new NullPointerException(); + throw new NullPointerException("element is null"); } } else { in = null; diff --git a/luni/src/main/java/java/io/StreamTokenizer.java b/luni/src/main/java/java/io/StreamTokenizer.java index 0522be6..a16dc4b 100644 --- a/luni/src/main/java/java/io/StreamTokenizer.java +++ b/luni/src/main/java/java/io/StreamTokenizer.java @@ -167,7 +167,7 @@ public class StreamTokenizer { public StreamTokenizer(InputStream is) { this(); if (is == null) { - throw new NullPointerException(); + throw new NullPointerException("is == null"); } inStream = is; } @@ -194,7 +194,7 @@ public class StreamTokenizer { public StreamTokenizer(Reader r) { this(); if (r == null) { - throw new NullPointerException(); + throw new NullPointerException("r == null"); } inReader = r; } diff --git a/luni/src/main/java/java/io/StringBufferInputStream.java b/luni/src/main/java/java/io/StringBufferInputStream.java index 1fada57..1768abe 100644 --- a/luni/src/main/java/java/io/StringBufferInputStream.java +++ b/luni/src/main/java/java/io/StringBufferInputStream.java @@ -54,7 +54,7 @@ public class StringBufferInputStream extends InputStream { */ public StringBufferInputStream(String str) { if (str == null) { - throw new NullPointerException(); + throw new NullPointerException("str == null"); } buffer = str; count = str.length(); diff --git a/luni/src/main/java/java/io/Writer.java b/luni/src/main/java/java/io/Writer.java index 2e28b80..33d7604 100644 --- a/luni/src/main/java/java/io/Writer.java +++ b/luni/src/main/java/java/io/Writer.java @@ -59,7 +59,7 @@ public abstract class Writer implements Appendable, Closeable, Flushable { */ protected Writer(Object lock) { if (lock == null) { - throw new NullPointerException(); + throw new NullPointerException("lock == null"); } this.lock = lock; } diff --git a/luni/src/main/java/java/lang/AbstractStringBuilder.java b/luni/src/main/java/java/lang/AbstractStringBuilder.java index baab47d..c3107f2 100644 --- a/luni/src/main/java/java/lang/AbstractStringBuilder.java +++ b/luni/src/main/java/java/lang/AbstractStringBuilder.java @@ -77,7 +77,7 @@ abstract class AbstractStringBuilder { AbstractStringBuilder(int capacity) { if (capacity < 0) { - throw new NegativeArraySizeException(); + throw new NegativeArraySizeException(Integer.toString(capacity)); } value = new char[capacity]; } @@ -437,7 +437,7 @@ abstract class AbstractStringBuilder { } if (start == end) { if (string == null) { - throw new NullPointerException(); + throw new NullPointerException("string == null"); } insert0(start, string); return; diff --git a/luni/src/main/java/java/lang/Character.java b/luni/src/main/java/java/lang/Character.java index 1a41ec2..cf0ab84 100644 --- a/luni/src/main/java/java/lang/Character.java +++ b/luni/src/main/java/java/lang/Character.java @@ -532,7 +532,7 @@ public final class Character implements Serializable, Comparable<Character> { */ protected Subset(String string) { if (string == null) { - throw new NullPointerException(); + throw new NullPointerException("string == null"); } name = string; } @@ -1502,7 +1502,7 @@ public final class Character implements Serializable, Comparable<Character> { */ public static UnicodeBlock forName(String blockName) { if (blockName == null) { - throw new NullPointerException(); + throw new NullPointerException("blockName == null"); } int block = forNameImpl(blockName); if (block == -1) { @@ -1798,7 +1798,7 @@ public final class Character implements Serializable, Comparable<Character> { */ public static int codePointAt(CharSequence seq, int index) { if (seq == null) { - throw new NullPointerException(); + throw new NullPointerException("seq == null"); } int len = seq.length(); if (index < 0 || index >= len) { @@ -1840,7 +1840,7 @@ public final class Character implements Serializable, Comparable<Character> { */ public static int codePointAt(char[] seq, int index) { if (seq == null) { - throw new NullPointerException(); + throw new NullPointerException("seq == null"); } int len = seq.length; if (index < 0 || index >= len) { @@ -1923,7 +1923,7 @@ public final class Character implements Serializable, Comparable<Character> { */ public static int codePointBefore(CharSequence seq, int index) { if (seq == null) { - throw new NullPointerException(); + throw new NullPointerException("seq == null"); } int len = seq.length(); if (index < 1 || index > len) { @@ -1965,7 +1965,7 @@ public final class Character implements Serializable, Comparable<Character> { */ public static int codePointBefore(char[] seq, int index) { if (seq == null) { - throw new NullPointerException(); + throw new NullPointerException("seq == null"); } int len = seq.length; if (index < 1 || index > len) { @@ -2012,7 +2012,7 @@ public final class Character implements Serializable, Comparable<Character> { */ public static int codePointBefore(char[] seq, int index, int start) { if (seq == null) { - throw new NullPointerException(); + throw new NullPointerException("seq == null"); } int len = seq.length; if (index <= start || index > len || start < 0 || start >= len) { @@ -2055,7 +2055,7 @@ public final class Character implements Serializable, Comparable<Character> { public static int toChars(int codePoint, char[] dst, int dstIndex) { checkValidCodePoint(codePoint); if (dst == null) { - throw new NullPointerException(); + throw new NullPointerException("dst == null"); } if (dstIndex < 0 || dstIndex >= dst.length) { throw new IndexOutOfBoundsException(); @@ -2126,7 +2126,7 @@ public final class Character implements Serializable, Comparable<Character> { public static int codePointCount(CharSequence seq, int beginIndex, int endIndex) { if (seq == null) { - throw new NullPointerException(); + throw new NullPointerException("seq == null"); } int len = seq.length(); if (beginIndex < 0 || endIndex > len || beginIndex > endIndex) { @@ -2215,7 +2215,7 @@ public final class Character implements Serializable, Comparable<Character> { */ public static int offsetByCodePoints(CharSequence seq, int index, int codePointOffset) { if (seq == null) { - throw new NullPointerException(); + throw new NullPointerException("seq == null"); } int len = seq.length(); if (index < 0 || index > len) { diff --git a/luni/src/main/java/java/lang/ClassLoader.java b/luni/src/main/java/java/lang/ClassLoader.java index 0cdc448..c99d57c 100644 --- a/luni/src/main/java/java/lang/ClassLoader.java +++ b/luni/src/main/java/java/lang/ClassLoader.java @@ -195,7 +195,7 @@ public abstract class ClassLoader { */ ClassLoader(ClassLoader parentLoader, boolean nullAllowed) { if (parentLoader == null && !nullAllowed) { - throw new NullPointerException("Parent ClassLoader may not be null"); + throw new NullPointerException("parentLoader == null && !nullAllowed"); } parent = parentLoader; } diff --git a/luni/src/main/java/java/lang/Daemons.java b/luni/src/main/java/java/lang/Daemons.java index a7d0964..78a4152 100644 --- a/luni/src/main/java/java/lang/Daemons.java +++ b/luni/src/main/java/java/lang/Daemons.java @@ -31,8 +31,9 @@ import libcore.util.EmptyArray; * @hide */ public final class Daemons { - private static final int NANOS_PER_MILLI = 1000000; - private static final long MAX_FINALIZE_MILLIS = 10L * 1000L; // 10 seconds + private static final int NANOS_PER_MILLI = 1000 * 1000; + private static final int NANOS_PER_SECOND = NANOS_PER_MILLI * 1000; + private static final long MAX_FINALIZE_NANOS = 10L * NANOS_PER_SECOND; public static void start() { ReferenceQueueDaemon.INSTANCE.start(); @@ -203,41 +204,78 @@ public final class Daemons { @Override public void run() { while (isRunning()) { - try { - Object object = FinalizerDaemon.INSTANCE.finalizingObject; - long startedNanos = FinalizerDaemon.INSTANCE.finalizingStartedNanos; - - if (object == null) { - synchronized (this) { - // wait until something is being finalized - // http://code.google.com/p/android/issues/detail?id=22778 - wait(); - continue; - } - } + Object object = waitForObject(); + if (object == null) { + // We have been interrupted, need to see if this daemon has been stopped. + continue; + } + boolean finalized = waitForFinalization(object); + if (!finalized && !VMRuntime.getRuntime().isDebuggerActive()) { + finalizerTimedOut(object); + break; + } + } + } - long elapsedMillis = (System.nanoTime() - startedNanos) / NANOS_PER_MILLI; - long sleepMillis = MAX_FINALIZE_MILLIS - elapsedMillis; - if (sleepMillis > 0) { - Thread.sleep(sleepMillis); - elapsedMillis = (System.nanoTime() - startedNanos) / NANOS_PER_MILLI; + private Object waitForObject() { + while (true) { + Object object = FinalizerDaemon.INSTANCE.finalizingObject; + if (object != null) { + return object; + } + synchronized (this) { + // wait until something is ready to be finalized + // http://code.google.com/p/android/issues/detail?id=22778 + try { + wait(); + } catch (InterruptedException e) { + // Daemon.stop may have interrupted us. + return null; } + } + } + } - if (object != FinalizerDaemon.INSTANCE.finalizingObject - || VMRuntime.getRuntime().isDebuggerActive()) { - continue; + private void sleepFor(long startNanos, long durationNanos) { + while (true) { + long elapsedNanos = System.nanoTime() - startNanos; + long sleepNanos = durationNanos - elapsedNanos; + long sleepMills = sleepNanos / NANOS_PER_MILLI; + if (sleepMills <= 0) { + return; + } + try { + Thread.sleep(sleepMills); + } catch (InterruptedException e) { + if (!isRunning()) { + return; } - - // The current object has exceeded the finalization deadline; abort! - Exception syntheticException = new TimeoutException(); - syntheticException.setStackTrace(FinalizerDaemon.INSTANCE.getStackTrace()); - System.logE(object.getClass().getName() + ".finalize() timed out after " - + elapsedMillis + " ms; limit is " + MAX_FINALIZE_MILLIS + " ms", - syntheticException); - System.exit(2); - } catch (InterruptedException ignored) { } } } + + private boolean waitForFinalization(Object object) { + sleepFor(FinalizerDaemon.INSTANCE.finalizingStartedNanos, MAX_FINALIZE_NANOS); + return object != FinalizerDaemon.INSTANCE.finalizingObject; + } + + private static void finalizerTimedOut(Object object) { + // The current object has exceeded the finalization deadline; abort! + String message = object.getClass().getName() + ".finalize() timed out after " + + (MAX_FINALIZE_NANOS / NANOS_PER_SECOND) + " seconds"; + Exception syntheticException = new TimeoutException(message); + // We use the stack from where finalize() was running to show where it was stuck. + syntheticException.setStackTrace(FinalizerDaemon.INSTANCE.getStackTrace()); + Thread.UncaughtExceptionHandler h = Thread.getDefaultUncaughtExceptionHandler(); + if (h == null) { + // If we have no handler, log and exit. + System.logE(message, syntheticException); + System.exit(2); + } + // Otherwise call the handler to do crash reporting. + // We don't just throw because we're not the thread that + // timed out; we're the thread that detected it. + h.uncaughtException(Thread.currentThread(), syntheticException); + } } } diff --git a/luni/src/main/java/java/lang/Enum.java b/luni/src/main/java/java/lang/Enum.java index 391670c..7a0f514 100644 --- a/luni/src/main/java/java/lang/Enum.java +++ b/luni/src/main/java/java/lang/Enum.java @@ -34,6 +34,9 @@ public abstract class Enum<E extends Enum<E>> implements Serializable, Comparabl private static final BasicLruCache<Class<? extends Enum>, Object[]> sharedConstantsCache = new BasicLruCache<Class<? extends Enum>, Object[]>(64) { @Override protected Object[] create(Class<? extends Enum> enumType) { + if (!enumType.isEnum()) { + return null; + } Method method = (Method) Class.getDeclaredConstructorOrMethod( enumType, "values", EmptyArray.CLASS); try { @@ -178,13 +181,16 @@ public abstract class Enum<E extends Enum<E>> implements Serializable, Comparabl * have a constant value called {@code name}. */ public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) { - if (enumType == null || name == null) { - throw new NullPointerException("enumType == null || name == null"); + if (enumType == null) { + throw new NullPointerException("enumType == null"); + } else if (name == null) { + throw new NullPointerException("name == null"); } - if (!enumType.isEnum()) { + T[] values = getSharedConstants(enumType); + if (values == null) { throw new IllegalArgumentException(enumType + " is not an enum type"); } - for (T value : getSharedConstants(enumType)) { + for (T value : values) { if (name.equals(value.name())) { return value; } diff --git a/luni/src/main/java/java/lang/Object.java b/luni/src/main/java/java/lang/Object.java index 7f4b490..4bca034 100644 --- a/luni/src/main/java/java/lang/Object.java +++ b/luni/src/main/java/java/lang/Object.java @@ -361,7 +361,7 @@ public class Object { * @see java.lang.Thread */ public final void wait() throws InterruptedException { - wait(0 ,0); + wait(0, 0); } /** diff --git a/luni/src/main/java/java/lang/ProcessBuilder.java b/luni/src/main/java/java/lang/ProcessBuilder.java index 5b7efdc..57e21b6 100644 --- a/luni/src/main/java/java/lang/ProcessBuilder.java +++ b/luni/src/main/java/java/lang/ProcessBuilder.java @@ -59,7 +59,7 @@ public final class ProcessBuilder { */ public ProcessBuilder(List<String> command) { if (command == null) { - throw new NullPointerException(); + throw new NullPointerException("command == null"); } this.command = command; @@ -102,7 +102,7 @@ public final class ProcessBuilder { */ public ProcessBuilder command(List<String> command) { if (command == null) { - throw new NullPointerException(); + throw new NullPointerException("command == null"); } this.command = command; return this; diff --git a/luni/src/main/java/java/lang/ProcessManager.java b/luni/src/main/java/java/lang/ProcessManager.java index 1e820a9..28314b7 100644 --- a/luni/src/main/java/java/lang/ProcessManager.java +++ b/luni/src/main/java/java/lang/ProcessManager.java @@ -168,10 +168,10 @@ final class ProcessManager { boolean redirectErrorStream) throws IOException { // Make sure we throw the same exceptions as the RI. if (taintedCommand == null) { - throw new NullPointerException(); + throw new NullPointerException("taintedCommand == null"); } if (taintedCommand.length == 0) { - throw new IndexOutOfBoundsException(); + throw new IndexOutOfBoundsException("taintedCommand.length == 0"); } // Handle security and safety by copying mutable inputs and checking them. @@ -179,16 +179,16 @@ final class ProcessManager { String[] environment = taintedEnvironment != null ? taintedEnvironment.clone() : null; // Check we're not passing null Strings to the native exec. - for (String arg : command) { - if (arg == null) { - throw new NullPointerException(); + for (int i = 0; i < command.length; i++) { + if (command[i] == null) { + throw new NullPointerException("taintedCommand[" + i + "] == null"); } } // The environment is allowed to be null or empty, but no element may be null. if (environment != null) { - for (String env : environment) { - if (env == null) { - throw new NullPointerException(); + for (int i = 0; i < environment.length; i++) { + if (environment[i] == null) { + throw new NullPointerException("taintedEnvironment[" + i + "] == null"); } } } diff --git a/luni/src/main/java/java/lang/Runtime.java b/luni/src/main/java/java/lang/Runtime.java index 320f157..a2debfd 100644 --- a/luni/src/main/java/java/lang/Runtime.java +++ b/luni/src/main/java/java/lang/Runtime.java @@ -224,9 +224,9 @@ public class Runtime { public Process exec(String prog, String[] envp, File directory) throws java.io.IOException { // Sanity checks if (prog == null) { - throw new NullPointerException(); - } else if (prog.length() == 0) { - throw new IllegalArgumentException(); + throw new NullPointerException("prog == null"); + } else if (prog.isEmpty()) { + throw new IllegalArgumentException("prog is empty"); } // Break down into tokens, as described in Java docs @@ -331,11 +331,11 @@ public class Runtime { /* * Loads and links a library without security checks. */ - void load(String filename, ClassLoader loader) { - if (filename == null) { - throw new NullPointerException("library path was null."); + void load(String pathName, ClassLoader loader) { + if (pathName == null) { + throw new NullPointerException("pathName == null"); } - String error = nativeLoad(filename, loader); + String error = nativeLoad(pathName, loader); if (error != null) { throw new UnsatisfiedLinkError(error); } @@ -362,8 +362,9 @@ public class Runtime { if (loader != null) { String filename = loader.findLibrary(libraryName); if (filename == null) { - throw new UnsatisfiedLinkError("Couldn't load " + libraryName + ": " + - "findLibrary returned null"); + throw new UnsatisfiedLinkError("Couldn't load " + libraryName + + " from loader " + loader + + ": findLibrary returned null"); } String error = nativeLoad(filename, loader); if (error != null) { @@ -537,7 +538,7 @@ public class Runtime { public void addShutdownHook(Thread hook) { // Sanity checks if (hook == null) { - throw new NullPointerException("Hook may not be null."); + throw new NullPointerException("hook == null"); } if (shuttingDown) { @@ -570,7 +571,7 @@ public class Runtime { public boolean removeShutdownHook(Thread hook) { // Sanity checks if (hook == null) { - throw new NullPointerException("Hook may not be null."); + throw new NullPointerException("hook == null"); } if (shuttingDown) { diff --git a/luni/src/main/java/java/lang/StackTraceElement.java b/luni/src/main/java/java/lang/StackTraceElement.java index b83120c..a59935a 100644 --- a/luni/src/main/java/java/lang/StackTraceElement.java +++ b/luni/src/main/java/java/lang/StackTraceElement.java @@ -58,8 +58,10 @@ public final class StackTraceElement implements Serializable { * if {@code cls} or {@code method} is {@code null}. */ public StackTraceElement(String cls, String method, String file, int line) { - if (cls == null || method == null) { - throw new NullPointerException(); + if (cls == null) { + throw new NullPointerException("cls == null"); + } else if (method == null) { + throw new NullPointerException("method == null"); } declaringClass = cls; methodName = method; diff --git a/luni/src/main/java/java/lang/String.java b/luni/src/main/java/java/lang/String.java index efd4210..f3aeb64 100644 --- a/luni/src/main/java/java/lang/String.java +++ b/luni/src/main/java/java/lang/String.java @@ -168,17 +168,7 @@ public final class String implements Serializable, Comparable<String>, CharSeque * if {@code byteCount < 0 || offset < 0 || offset + byteCount > data.length}. */ public String(byte[] data, int offset, int byteCount) { - if ((offset | byteCount) < 0 || byteCount > data.length - offset) { - throw failedBoundsCheck(data.length, offset, byteCount); - } - CharBuffer cb = Charset.defaultCharset().decode(ByteBuffer.wrap(data, offset, byteCount)); - this.count = cb.length(); - this.offset = 0; - if (count > 0) { - value = cb.array(); - } else { - value = EmptyArray.CHAR; - } + this(data, offset, byteCount, Charset.defaultCharset()); } /** @@ -524,7 +514,7 @@ outer: */ public String(int[] codePoints, int offset, int count) { if (codePoints == null) { - throw new NullPointerException(); + throw new NullPointerException("codePoints == null"); } if ((offset | count) < 0 || count > codePoints.length - offset) { throw failedBoundsCheck(codePoints.length, offset, count); @@ -1232,7 +1222,7 @@ outer: */ public boolean regionMatches(int thisStart, String string, int start, int length) { if (string == null) { - throw new NullPointerException(); + throw new NullPointerException("string == null"); } if (start < 0 || string.count - start < length) { return false; @@ -1729,7 +1719,7 @@ outer: */ public boolean contentEquals(CharSequence cs) { if (cs == null) { - throw new NullPointerException(); + throw new NullPointerException("cs == null"); } int len = cs.length(); @@ -1922,7 +1912,7 @@ outer: */ public boolean contains(CharSequence cs) { if (cs == null) { - throw new NullPointerException(); + throw new NullPointerException("cs == null"); } return indexOf(cs.toString()) >= 0; } @@ -1991,7 +1981,7 @@ outer: */ public static String format(Locale locale, String format, Object... args) { if (format == null) { - throw new NullPointerException("null format argument"); + throw new NullPointerException("format == null"); } int bufferSize = format.length() + (args == null ? 0 : args.length * 10); Formatter f = new Formatter(new StringBuilder(bufferSize), locale); diff --git a/luni/src/main/java/java/lang/StringBuilder.java b/luni/src/main/java/java/lang/StringBuilder.java index d886100..a944e68 100644 --- a/luni/src/main/java/java/lang/StringBuilder.java +++ b/luni/src/main/java/java/lang/StringBuilder.java @@ -624,7 +624,7 @@ public final class StringBuilder extends AbstractStringBuilder implements * the inclusive begin index. * @param end * the exclusive end index. - * @param str + * @param string * the replacement string. * @return this builder. * @throws StringIndexOutOfBoundsException @@ -633,8 +633,8 @@ public final class StringBuilder extends AbstractStringBuilder implements * @throws NullPointerException * if {@code str} is {@code null}. */ - public StringBuilder replace(int start, int end, String str) { - replace0(start, end, str); + public StringBuilder replace(int start, int end, String string) { + replace0(start, end, string); return this; } diff --git a/luni/src/main/java/java/lang/System.java b/luni/src/main/java/java/lang/System.java index 24ebf6b..df84c61 100644 --- a/luni/src/main/java/java/lang/System.java +++ b/luni/src/main/java/java/lang/System.java @@ -34,6 +34,7 @@ package java.lang; import dalvik.system.VMRuntime; import dalvik.system.VMStack; +import java.io.BufferedInputStream; import java.io.Console; import java.io.FileDescriptor; import java.io.FileInputStream; @@ -83,10 +84,9 @@ public final class System { private static Properties systemProperties; static { - // TODO: all three streams are buffered in Harmony. err = new PrintStream(new FileOutputStream(FileDescriptor.err)); out = new PrintStream(new FileOutputStream(FileDescriptor.out)); - in = new FileInputStream(FileDescriptor.in); + in = new BufferedInputStream(new FileInputStream(FileDescriptor.in)); lineSeparator = System.getProperty("line.separator"); } @@ -455,7 +455,7 @@ public final class System { */ public static String clearProperty(String key) { if (key == null) { - throw new NullPointerException(); + throw new NullPointerException("key == null"); } if (key.isEmpty()) { throw new IllegalArgumentException(); @@ -679,7 +679,7 @@ public final class System { private String toNonNullString(Object o) { if (o == null) { - throw new NullPointerException(); + throw new NullPointerException("o == null"); } return (String) o; } diff --git a/luni/src/main/java/java/lang/Thread.java b/luni/src/main/java/java/lang/Thread.java index a61f669..210b90c 100644 --- a/luni/src/main/java/java/lang/Thread.java +++ b/luni/src/main/java/java/lang/Thread.java @@ -232,7 +232,7 @@ public class Thread implements Runnable { */ public Thread(Runnable runnable, String threadName) { if (threadName == null) { - throw new NullPointerException(); + throw new NullPointerException("threadName == null"); } create(null, runnable, threadName, 0); @@ -252,7 +252,7 @@ public class Thread implements Runnable { */ public Thread(String threadName) { if (threadName == null) { - throw new NullPointerException(); + throw new NullPointerException("threadName == null"); } create(null, null, threadName, 0); @@ -296,7 +296,7 @@ public class Thread implements Runnable { */ public Thread(ThreadGroup group, Runnable runnable, String threadName) { if (threadName == null) { - throw new NullPointerException(); + throw new NullPointerException("threadName == null"); } create(group, runnable, threadName, 0); @@ -317,7 +317,7 @@ public class Thread implements Runnable { */ public Thread(ThreadGroup group, String threadName) { if (threadName == null) { - throw new NullPointerException(); + throw new NullPointerException("threadName == null"); } create(group, null, threadName, 0); @@ -346,7 +346,7 @@ public class Thread implements Runnable { */ public Thread(ThreadGroup group, Runnable runnable, String threadName, long stackSize) { if (threadName == null) { - throw new NullPointerException(); + throw new NullPointerException("threadName == null"); } create(group, runnable, threadName, stackSize); } @@ -943,7 +943,7 @@ public class Thread implements Runnable { */ public final void setName(String threadName) { if (threadName == null) { - throw new NullPointerException(); + throw new NullPointerException("threadName == null"); } name = threadName; diff --git a/luni/src/main/java/java/lang/Throwable.java b/luni/src/main/java/java/lang/Throwable.java index b561832..b20b882 100644 --- a/luni/src/main/java/java/lang/Throwable.java +++ b/luni/src/main/java/java/lang/Throwable.java @@ -23,6 +23,7 @@ import java.io.ObjectOutputStream; import java.io.PrintStream; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import libcore.util.EmptyArray; @@ -63,13 +64,13 @@ public class Throwable implements java.io.Serializable { * Throwables suppressed by this throwable. Null when suppressed exceptions * are disabled. */ - private List<Throwable> suppressedExceptions = new ArrayList<Throwable>(); + private List<Throwable> suppressedExceptions = Collections.emptyList(); /** * An intermediate representation of the stack trace. This field may * be accessed by the VM; do not rename. */ - private volatile Object stackState; + private transient volatile Object stackState; /** * A fully-expanded representation of the stack trace. @@ -218,9 +219,9 @@ public class Throwable implements java.io.Serializable { */ public void setStackTrace(StackTraceElement[] trace) { StackTraceElement[] newTrace = trace.clone(); - for (StackTraceElement element : newTrace) { - if (element == null) { - throw new NullPointerException(); + for (int i = 0; i < newTrace.length; i++) { + if (newTrace[i] == null) { + throw new NullPointerException("trace[" + i + "] == null"); } } stackTrace = newTrace; @@ -412,12 +413,17 @@ public class Throwable implements java.io.Serializable { */ public final void addSuppressed(Throwable throwable) { if (throwable == this) { - throw new IllegalArgumentException("suppressed == this"); + throw new IllegalArgumentException("throwable == this"); } if (throwable == null) { - throw new NullPointerException("suppressed == null"); + throw new NullPointerException("throwable == null"); } if (suppressedExceptions != null) { + // suppressed exceptions are enabled + if (suppressedExceptions.isEmpty()) { + // ensure we have somewhere to place suppressed exceptions + suppressedExceptions = new ArrayList<Throwable>(1); + } suppressedExceptions.add(throwable); } } @@ -429,7 +435,7 @@ public class Throwable implements java.io.Serializable { * @hide 1.7 */ public final Throwable[] getSuppressed() { - return (suppressedExceptions != null) + return (suppressedExceptions != null && !suppressedExceptions.isEmpty()) ? suppressedExceptions.toArray(new Throwable[suppressedExceptions.size()]) : EmptyArray.THROWABLE; } diff --git a/luni/src/main/java/java/lang/ref/FinalizerReference.java b/luni/src/main/java/java/lang/ref/FinalizerReference.java index aadf1f6..14eaae4 100644 --- a/luni/src/main/java/java/lang/ref/FinalizerReference.java +++ b/luni/src/main/java/java/lang/ref/FinalizerReference.java @@ -20,33 +20,39 @@ package java.lang.ref; * @hide */ public final class FinalizerReference<T> extends Reference<T> { + // This queue contains those objects eligible for finalization. public static final ReferenceQueue<Object> queue = new ReferenceQueue<Object>(); - private static FinalizerReference head = null; + // Guards the list (not the queue). + private static final Object LIST_LOCK = new Object(); - private T zombie; + // This list contains a FinalizerReference for every finalizable object in the heap. + // Objects in this list may or may not be eligible for finalization yet. + private static FinalizerReference<?> head = null; - private FinalizerReference prev; + // The links used to construct the list. + private FinalizerReference<?> prev; + private FinalizerReference<?> next; - private FinalizerReference next; + // When the GC wants something finalized, it moves it from the 'referent' field to + // the 'zombie' field instead. + private T zombie; public FinalizerReference(T r, ReferenceQueue<? super T> q) { super(r, q); } - @Override - public T get() { + @Override public T get() { return zombie; } - @Override - public void clear() { + @Override public void clear() { zombie = null; } - static void add(Object referent) { + public static void add(Object referent) { FinalizerReference<?> reference = new FinalizerReference<Object>(referent, queue); - synchronized (FinalizerReference.class) { + synchronized (LIST_LOCK) { reference.prev = null; reference.next = head; if (head != null) { @@ -56,10 +62,10 @@ public final class FinalizerReference<T> extends Reference<T> { } } - public static void remove(FinalizerReference reference) { - synchronized (FinalizerReference.class) { - FinalizerReference next = reference.next; - FinalizerReference prev = reference.prev; + public static void remove(FinalizerReference<?> reference) { + synchronized (LIST_LOCK) { + FinalizerReference<?> next = reference.next; + FinalizerReference<?> prev = reference.prev; reference.next = null; reference.prev = null; if (prev != null) { @@ -74,30 +80,50 @@ public final class FinalizerReference<T> extends Reference<T> { } /** - * Returns once all currently-enqueued references have been finalized. + * Waits for all currently-enqueued references to be finalized. */ public static void finalizeAllEnqueued() throws InterruptedException { Sentinel sentinel = new Sentinel(); - FinalizerReference<Object> reference = new FinalizerReference<Object>(null, queue); - reference.zombie = sentinel; - reference.enqueueInternal(); + enqueueSentinelReference(sentinel); sentinel.awaitFinalization(); } + private static void enqueueSentinelReference(Sentinel sentinel) { + synchronized (LIST_LOCK) { + // When a finalizable object is allocated, a FinalizerReference is added to the list. + // We search the list for that FinalizerReference (it should be at or near the head), + // and then put it on the queue so that it can be finalized. + for (FinalizerReference<?> r = head; r != null; r = r.next) { + if (r.referent == sentinel) { + FinalizerReference<Sentinel> sentinelReference = (FinalizerReference<Sentinel>) r; + sentinelReference.referent = null; + sentinelReference.zombie = sentinel; + sentinelReference.enqueueInternal(); + return; + } + } + } + // We just created a finalizable object and still hold a reference to it. + // It must be on the list. + throw new AssertionError("newly-created live Sentinel not on list!"); + } + /** * A marker object that we can immediately enqueue. When this object's * finalize() method is called, we know all previously-enqueued finalizable * references have been finalized. - * - * <p>Each instance of this class will be finalized twice as it is enqueued - * directly and by the garbage collector. */ private static class Sentinel { boolean finalized = false; + @Override protected synchronized void finalize() throws Throwable { + if (finalized) { + throw new AssertionError(); + } finalized = true; notifyAll(); } + synchronized void awaitFinalization() throws InterruptedException { while (!finalized) { wait(); diff --git a/luni/src/main/java/java/lang/ref/Reference.java b/luni/src/main/java/java/lang/ref/Reference.java index 85fbb04..9cf49a7 100644 --- a/luni/src/main/java/java/lang/ref/Reference.java +++ b/luni/src/main/java/java/lang/ref/Reference.java @@ -55,8 +55,7 @@ public abstract class Reference<T> { * VM requirement: this field <em>must</em> be called "queue" * and be a java.lang.ref.ReferenceQueue. */ - @SuppressWarnings("unchecked") - volatile ReferenceQueue queue; + volatile ReferenceQueue<? super T> queue; /** * Used internally by java.lang.ref.ReferenceQueue. @@ -82,7 +81,7 @@ public abstract class Reference<T> { Reference() { } - Reference(T r, ReferenceQueue q) { + Reference(T r, ReferenceQueue<? super T> q) { referent = r; queue = q; } diff --git a/luni/src/main/java/java/lang/ref/ReferenceQueue.java b/luni/src/main/java/java/lang/ref/ReferenceQueue.java index 6c9b4d5..2b8089c 100644 --- a/luni/src/main/java/java/lang/ref/ReferenceQueue.java +++ b/luni/src/main/java/java/lang/ref/ReferenceQueue.java @@ -131,8 +131,6 @@ public class ReferenceQueue<T> { * * @param reference * reference object to be enqueued. - * @return boolean true if reference is enqueued. false if reference failed - * to enqueue. */ synchronized void enqueue(Reference<? extends T> reference) { if (head == null) { @@ -145,7 +143,7 @@ public class ReferenceQueue<T> { } /** @hide */ - public static Reference unenqueued = null; + public static Reference<?> unenqueued = null; static void add(Reference<?> list) { synchronized (ReferenceQueue.class) { diff --git a/luni/src/main/java/java/lang/reflect/GenericArrayType.java b/luni/src/main/java/java/lang/reflect/GenericArrayType.java index 6344019..fc03f78 100644 --- a/luni/src/main/java/java/lang/reflect/GenericArrayType.java +++ b/luni/src/main/java/java/lang/reflect/GenericArrayType.java @@ -36,4 +36,4 @@ public interface GenericArrayType extends Type { * instantiated for some reason */ Type getGenericComponentType(); -}
\ No newline at end of file +} diff --git a/luni/src/main/java/java/lang/reflect/Proxy.java b/luni/src/main/java/java/lang/reflect/Proxy.java index a24514d..3b10887 100644 --- a/luni/src/main/java/java/lang/reflect/Proxy.java +++ b/luni/src/main/java/java/lang/reflect/Proxy.java @@ -89,13 +89,13 @@ public class Proxy implements Serializable { Class<?>... interfaces) throws IllegalArgumentException { // check that interfaces are a valid array of visible interfaces if (interfaces == null) { - throw new NullPointerException(); + throw new NullPointerException("interfaces == null"); } String commonPackageName = null; for (int i = 0, length = interfaces.length; i < length; i++) { Class<?> next = interfaces[i]; if (next == null) { - throw new NullPointerException(); + throw new NullPointerException("interfaces[" + i + "] == null"); } String name = next.getName(); if (!next.isInterface()) { @@ -206,7 +206,7 @@ public class Proxy implements Serializable { Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { if (h == null) { - throw new NullPointerException(); + throw new NullPointerException("h == null"); } try { return getProxyClass(loader, interfaces).getConstructor( @@ -241,7 +241,7 @@ public class Proxy implements Serializable { */ public static boolean isProxyClass(Class<?> cl) { if (cl == null) { - throw new NullPointerException(); + throw new NullPointerException("cl == null"); } synchronized (proxyCache) { return proxyCache.containsKey(cl); diff --git a/luni/src/main/java/java/math/BigDecimal.java b/luni/src/main/java/java/math/BigDecimal.java index 3a5f3cd..335e3bc 100644 --- a/luni/src/main/java/java/math/BigDecimal.java +++ b/luni/src/main/java/java/math/BigDecimal.java @@ -276,7 +276,7 @@ public class BigDecimal extends Number implements Comparable<BigDecimal>, Serial long newScale; // the new scale if (in == null) { - throw new NullPointerException(); + throw new NullPointerException("in == null"); } if ((last >= in.length) || (offset < 0) || (len <= 0) || (last < 0)) { throw new NumberFormatException("Bad offset/length: offset=" + offset + @@ -601,7 +601,7 @@ public class BigDecimal extends Number implements Comparable<BigDecimal>, Serial */ public BigDecimal(BigInteger unscaledVal, int scale) { if (unscaledVal == null) { - throw new NullPointerException(); + throw new NullPointerException("unscaledVal == null"); } this.scale = scale; setUnscaledValue(unscaledVal); @@ -1059,7 +1059,7 @@ public class BigDecimal extends Number implements Comparable<BigDecimal>, Serial public BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode) { // Let be: this = [u1,s1] and divisor = [u2,s2] if (roundingMode == null) { - throw new NullPointerException(); + throw new NullPointerException("roundingMode == null"); } if (divisor.isZero()) { throw new ArithmeticException("Division by zero"); @@ -1916,7 +1916,7 @@ public class BigDecimal extends Number implements Comparable<BigDecimal>, Serial */ public BigDecimal setScale(int newScale, RoundingMode roundingMode) { if (roundingMode == null) { - throw new NullPointerException(); + throw new NullPointerException("roundingMode == null"); } long diffScale = newScale - (long)scale; // Let be: 'this' = [u,s] diff --git a/luni/src/main/java/java/math/BigInt.java b/luni/src/main/java/java/math/BigInt.java index 1768676..614dbb4 100644 --- a/luni/src/main/java/java/math/BigInt.java +++ b/luni/src/main/java/java/math/BigInt.java @@ -153,7 +153,7 @@ final class BigInt { */ String checkString(String s, int base) { if (s == null) { - throw new NullPointerException(); + throw new NullPointerException("s == null"); } // A valid big integer consists of an optional '-' or '+' followed by // one or more digit characters appropriate to the given base, diff --git a/luni/src/main/java/java/net/CookieStore.java b/luni/src/main/java/java/net/CookieStore.java index d09b7e8..619d65c 100644 --- a/luni/src/main/java/java/net/CookieStore.java +++ b/luni/src/main/java/java/net/CookieStore.java @@ -100,4 +100,4 @@ public interface CookieStore { * @return true if any cookies were removed as a result of this call. */ boolean removeAll(); -}
\ No newline at end of file +} diff --git a/luni/src/main/java/java/net/DatagramSocket.java b/luni/src/main/java/java/net/DatagramSocket.java index 2b468fa..c01f3af 100644 --- a/luni/src/main/java/java/net/DatagramSocket.java +++ b/luni/src/main/java/java/net/DatagramSocket.java @@ -244,7 +244,7 @@ public class DatagramSocket { checkOpen(); ensureBound(); if (pack == null) { - throw new NullPointerException(); + throw new NullPointerException("pack == null"); } if (pendingConnectException != null) { throw new SocketException("Pending connect failure", pendingConnectException); @@ -295,7 +295,7 @@ public class DatagramSocket { */ public void setNetworkInterface(NetworkInterface netInterface) throws SocketException { if (netInterface == null) { - throw new NullPointerException("networkInterface == null"); + throw new NullPointerException("netInterface == null"); } try { Libcore.os.setsockoptIfreq(impl.fd, SOL_SOCKET, SO_BINDTODEVICE, netInterface.getName()); @@ -374,7 +374,7 @@ public class DatagramSocket { */ protected DatagramSocket(DatagramSocketImpl socketImpl) { if (socketImpl == null) { - throw new NullPointerException(); + throw new NullPointerException("socketImpl == null"); } impl = socketImpl; } diff --git a/luni/src/main/java/java/net/NetworkInterface.java b/luni/src/main/java/java/net/NetworkInterface.java index e06b811..ad81f32 100644 --- a/luni/src/main/java/java/net/NetworkInterface.java +++ b/luni/src/main/java/java/net/NetworkInterface.java @@ -122,7 +122,9 @@ public final class NetworkInterface extends Object { private static void collectIpv6Addresses(String interfaceName, int interfaceIndex, List<InetAddress> addresses, List<InterfaceAddress> interfaceAddresses) throws SocketException { - // Format of /proc/net/if_inet6 (all numeric fields are implicit hex). + // Format of /proc/net/if_inet6. + // All numeric fields are implicit hex, + // but not necessarily two-digit (http://code.google.com/p/android/issues/detail?id=34022). // 1. IPv6 address // 2. interface index // 3. prefix length @@ -130,6 +132,7 @@ public final class NetworkInterface extends Object { // 5. flags // 6. interface name // "00000000000000000000000000000001 01 80 10 80 lo" + // "fe800000000000000000000000000000 407 40 20 80 wlan0" BufferedReader in = null; try { in = new BufferedReader(new FileReader("/proc/net/if_inet6")); @@ -139,13 +142,22 @@ public final class NetworkInterface extends Object { if (!line.endsWith(suffix)) { continue; } + + // Extract the IPv6 address. byte[] addressBytes = new byte[16]; for (int i = 0; i < addressBytes.length; ++i) { addressBytes[i] = (byte) Integer.parseInt(line.substring(2*i, 2*i + 2), 16); } - short prefixLength = Short.parseShort(line.substring(36, 38), 16); - Inet6Address inet6Address = new Inet6Address(addressBytes, null, interfaceIndex); + // Extract the prefix length. + // Skip the IPv6 address and its trailing space. + int prefixLengthStart = 32 + 1; + // Skip the interface index and its trailing space. + prefixLengthStart = line.indexOf(' ', prefixLengthStart) + 1; + int prefixLengthEnd = line.indexOf(' ', prefixLengthStart); + short prefixLength = Short.parseShort(line.substring(prefixLengthStart, prefixLengthEnd), 16); + + Inet6Address inet6Address = new Inet6Address(addressBytes, null, interfaceIndex); addresses.add(inet6Address); interfaceAddresses.add(new InterfaceAddress(inet6Address, prefixLength)); } diff --git a/luni/src/main/java/java/net/PlainDatagramSocketImpl.java b/luni/src/main/java/java/net/PlainDatagramSocketImpl.java index 5d01469..3226527 100644 --- a/luni/src/main/java/java/net/PlainDatagramSocketImpl.java +++ b/luni/src/main/java/java/net/PlainDatagramSocketImpl.java @@ -216,6 +216,8 @@ public class PlainDatagramSocketImpl extends DatagramSocketImpl { Libcore.os.connect(fd, InetAddress.UNSPECIFIED, 0); } catch (ErrnoException errnoException) { throw new AssertionError(errnoException); + } catch (SocketException ignored) { + // Thrown if the socket has already been closed, but this method can't throw anything. } connectedPort = -1; connectedAddress = null; diff --git a/luni/src/main/java/java/net/URISyntaxException.java b/luni/src/main/java/java/net/URISyntaxException.java index e08f444..957ea31 100644 --- a/luni/src/main/java/java/net/URISyntaxException.java +++ b/luni/src/main/java/java/net/URISyntaxException.java @@ -49,8 +49,10 @@ public class URISyntaxException extends Exception { public URISyntaxException(String input, String reason, int index) { super(reason); - if (input == null || reason == null) { - throw new NullPointerException(); + if (input == null) { + throw new NullPointerException("input == null"); + } else if (reason == null) { + throw new NullPointerException("reason == null"); } if (index < -1) { @@ -76,8 +78,10 @@ public class URISyntaxException extends Exception { public URISyntaxException(String input, String reason) { super(reason); - if (input == null || reason == null) { - throw new NullPointerException(); + if (input == null) { + throw new NullPointerException("input == null"); + } else if (reason == null) { + throw new NullPointerException("reason == null"); } this.input = input; diff --git a/luni/src/main/java/java/net/URLClassLoader.java b/luni/src/main/java/java/net/URLClassLoader.java index 1c8bc43..efb7531 100644 --- a/luni/src/main/java/java/net/URLClassLoader.java +++ b/luni/src/main/java/java/net/URLClassLoader.java @@ -822,7 +822,7 @@ public class URLClassLoader extends SecureClassLoader { while (!searchList.isEmpty()) { URL nextCandidate = searchList.remove(0); if (nextCandidate == null) { - throw new NullPointerException("A URL is null"); + throw new NullPointerException("nextCandidate == null"); } if (!handlerMap.containsKey(nextCandidate)) { URLHandler result; diff --git a/luni/src/main/java/java/net/URLConnection.java b/luni/src/main/java/java/net/URLConnection.java index c832bfb..18a264e 100644 --- a/luni/src/main/java/java/net/URLConnection.java +++ b/luni/src/main/java/java/net/URLConnection.java @@ -948,11 +948,12 @@ public abstract class URLConnection { } /** - * Sets the maximum time to wait for a connect to complete before giving up. + * Sets the maximum time in milliseconds to wait while connecting. * Connecting to a server will fail with a {@link SocketTimeoutException} if * the timeout elapses before a connection is established. The default value - * of {@code 0} disables connect timeouts; connect attempts may wait - * indefinitely. + * of {@code 0} causes us to do a blocking connect. This does not mean we + * will never time out, but it probably means you'll get a TCP timeout + * after several minutes. * * <p><strong>Warning:</strong> if the hostname resolves to multiple IP * addresses, this client will try each in <a @@ -961,7 +962,7 @@ public abstract class URLConnection { * elapse before the connect attempt throws an exception. Host names that * support both IPv6 and IPv4 always have at least 2 IP addresses. * - * @param timeoutMillis the connect timeout in milliseconds. Non-negative. + * @throws IllegalArgumentException if {@code timeoutMillis < 0}. */ public void setConnectTimeout(int timeoutMillis) { if (timeoutMillis < 0) { @@ -971,8 +972,7 @@ public abstract class URLConnection { } /** - * Returns the connect timeout in milliseconds, or {@code 0} if connect - * attempts never timeout. + * Returns the connect timeout in milliseconds. (See {#setConnectTimeout}.) */ public int getConnectTimeout() { return connectTimeout; diff --git a/luni/src/main/java/java/nio/Buffer.java b/luni/src/main/java/java/nio/Buffer.java index c3840a5..b90744b 100644 --- a/luni/src/main/java/java/nio/Buffer.java +++ b/luni/src/main/java/java/nio/Buffer.java @@ -271,7 +271,7 @@ public abstract class Buffer { final void checkWritable() { if (isReadOnly()) { - throw new IllegalArgumentException("read-only buffer"); + throw new IllegalArgumentException("Read-only buffer"); } } diff --git a/luni/src/main/java/java/nio/ByteBuffer.java b/luni/src/main/java/java/nio/ByteBuffer.java index ef725c1..6a3624a 100644 --- a/luni/src/main/java/java/nio/ByteBuffer.java +++ b/luni/src/main/java/java/nio/ByteBuffer.java @@ -47,7 +47,7 @@ public abstract class ByteBuffer extends Buffer implements Comparable<ByteBuffer */ public static ByteBuffer allocate(int capacity) { if (capacity < 0) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("capacity < 0: " + capacity); } return new ReadWriteHeapByteBuffer(capacity); } @@ -63,7 +63,7 @@ public abstract class ByteBuffer extends Buffer implements Comparable<ByteBuffer */ public static ByteBuffer allocateDirect(int capacity) { if (capacity < 0) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("capacity < 0: " + capacity); } return new ReadWriteDirectByteBuffer(capacity); } diff --git a/luni/src/main/java/java/nio/CharBuffer.java b/luni/src/main/java/java/nio/CharBuffer.java index 03bac04..6429ae2 100644 --- a/luni/src/main/java/java/nio/CharBuffer.java +++ b/luni/src/main/java/java/nio/CharBuffer.java @@ -49,7 +49,7 @@ public abstract class CharBuffer extends Buffer implements */ public static CharBuffer allocate(int capacity) { if (capacity < 0) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("capacity < 0: " + capacity); } return new ReadWriteCharArrayBuffer(capacity); } @@ -500,7 +500,7 @@ public abstract class CharBuffer extends Buffer implements */ public CharBuffer put(CharBuffer src) { if (src == this) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("src == this"); } if (src.remaining() > remaining()) { throw new BufferOverflowException(); @@ -734,7 +734,7 @@ public abstract class CharBuffer extends Buffer implements if (remaining == 0) { return -1; } - throw new IllegalArgumentException(); + throw new IllegalArgumentException("target == this"); } if (remaining == 0) { return limit > 0 && target.remaining() == 0 ? 0 : -1; diff --git a/luni/src/main/java/java/nio/DatagramChannelImpl.java b/luni/src/main/java/java/nio/DatagramChannelImpl.java index a0e064d..4d2fc5a 100644 --- a/luni/src/main/java/java/nio/DatagramChannelImpl.java +++ b/luni/src/main/java/java/nio/DatagramChannelImpl.java @@ -448,7 +448,7 @@ class DatagramChannelImpl extends DatagramChannel implements FileDescriptorChann */ private void checkNotNull(ByteBuffer source) { if (source == null) { - throw new NullPointerException(); + throw new NullPointerException("source == null"); } } diff --git a/luni/src/main/java/java/nio/DoubleBuffer.java b/luni/src/main/java/java/nio/DoubleBuffer.java index c495592..8d90f89 100644 --- a/luni/src/main/java/java/nio/DoubleBuffer.java +++ b/luni/src/main/java/java/nio/DoubleBuffer.java @@ -47,7 +47,7 @@ public abstract class DoubleBuffer extends Buffer implements */ public static DoubleBuffer allocate(int capacity) { if (capacity < 0) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("capacity < 0: " + capacity); } return new ReadWriteDoubleArrayBuffer(capacity); } @@ -438,7 +438,7 @@ public abstract class DoubleBuffer extends Buffer implements */ public DoubleBuffer put(DoubleBuffer src) { if (src == this) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("src == this"); } if (src.remaining() > remaining()) { throw new BufferOverflowException(); diff --git a/luni/src/main/java/java/nio/FileChannelImpl.java b/luni/src/main/java/java/nio/FileChannelImpl.java index 76bad51..075243a 100644 --- a/luni/src/main/java/java/nio/FileChannelImpl.java +++ b/luni/src/main/java/java/nio/FileChannelImpl.java @@ -442,7 +442,7 @@ final class FileChannelImpl extends FileChannel { public FileChannel truncate(long size) throws IOException { checkOpen(); if (size < 0) { - throw new IllegalArgumentException("size: " + size); + throw new IllegalArgumentException("size < 0: " + size); } checkWritable(); if (size < size()) { @@ -457,7 +457,7 @@ final class FileChannelImpl extends FileChannel { public int write(ByteBuffer buffer, long position) throws IOException { if (position < 0) { - throw new IllegalArgumentException("position: " + position); + throw new IllegalArgumentException("position < 0: " + position); } return writeImpl(buffer, position); } diff --git a/luni/src/main/java/java/nio/FloatBuffer.java b/luni/src/main/java/java/nio/FloatBuffer.java index ec361d6..814eb53 100644 --- a/luni/src/main/java/java/nio/FloatBuffer.java +++ b/luni/src/main/java/java/nio/FloatBuffer.java @@ -46,7 +46,7 @@ public abstract class FloatBuffer extends Buffer implements */ public static FloatBuffer allocate(int capacity) { if (capacity < 0) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("capacity < 0: " + capacity); } return new ReadWriteFloatArrayBuffer(capacity); } @@ -437,7 +437,7 @@ public abstract class FloatBuffer extends Buffer implements */ public FloatBuffer put(FloatBuffer src) { if (src == this) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("src == this"); } if (src.remaining() > remaining()) { throw new BufferOverflowException(); diff --git a/luni/src/main/java/java/nio/IntBuffer.java b/luni/src/main/java/java/nio/IntBuffer.java index 9cc05ff..0ff758a 100644 --- a/luni/src/main/java/java/nio/IntBuffer.java +++ b/luni/src/main/java/java/nio/IntBuffer.java @@ -44,7 +44,7 @@ public abstract class IntBuffer extends Buffer implements Comparable<IntBuffer> */ public static IntBuffer allocate(int capacity) { if (capacity < 0) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("capacity < 0: " + capacity); } return new ReadWriteIntArrayBuffer(capacity); } @@ -423,7 +423,7 @@ public abstract class IntBuffer extends Buffer implements Comparable<IntBuffer> */ public IntBuffer put(IntBuffer src) { if (src == this) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("src == this"); } if (src.remaining() > remaining()) { throw new BufferOverflowException(); diff --git a/luni/src/main/java/java/nio/LongBuffer.java b/luni/src/main/java/java/nio/LongBuffer.java index 27edd2e..1254ddb 100644 --- a/luni/src/main/java/java/nio/LongBuffer.java +++ b/luni/src/main/java/java/nio/LongBuffer.java @@ -46,7 +46,7 @@ public abstract class LongBuffer extends Buffer implements */ public static LongBuffer allocate(int capacity) { if (capacity < 0) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("capacity < 0: " + capacity); } return new ReadWriteLongArrayBuffer(capacity); } @@ -427,7 +427,7 @@ public abstract class LongBuffer extends Buffer implements */ public LongBuffer put(LongBuffer src) { if (src == this) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("src == this"); } if (src.remaining() > remaining()) { throw new BufferOverflowException(); diff --git a/luni/src/main/java/java/nio/MappedByteBuffer.java b/luni/src/main/java/java/nio/MappedByteBuffer.java index 39c4986..0e8bf09 100644 --- a/luni/src/main/java/java/nio/MappedByteBuffer.java +++ b/luni/src/main/java/java/nio/MappedByteBuffer.java @@ -45,7 +45,7 @@ public abstract class MappedByteBuffer extends ByteBuffer { MappedByteBuffer(ByteBuffer directBuffer) { super(directBuffer.capacity, directBuffer.block); if (!directBuffer.isDirect()) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("directBuffer is not a direct buffer: " + directBuffer); } this.wrapped = (DirectByteBuffer) directBuffer; this.mapMode = null; diff --git a/luni/src/main/java/java/nio/NIOAccess.java b/luni/src/main/java/java/nio/NIOAccess.java index 361a37f..36c41cf 100644 --- a/luni/src/main/java/java/nio/NIOAccess.java +++ b/luni/src/main/java/java/nio/NIOAccess.java @@ -28,7 +28,7 @@ final class NIOAccess { * different than what the Harmony implementation calls a "base * address." * - * @param Buffer b the Buffer to be queried + * @param b the Buffer to be queried * @return the native pointer to the Buffer's data at its current * position, or 0 if there is none */ @@ -44,7 +44,7 @@ final class NIOAccess { * Returns the underlying Java array containing the data of the * given Buffer, or null if the Buffer is not backed by a Java array. * - * @param Buffer b the Buffer to be queried + * @param b the Buffer to be queried * @return the Java array containing the Buffer's data, or null if * there is none */ @@ -55,13 +55,14 @@ final class NIOAccess { /** * Returns the offset in bytes from the start of the underlying * Java array object containing the data of the given Buffer to - * the actual start of the data. This method is only meaningful if - * getBaseArray() returns non-null. + * the actual start of the data. The start of the data takes into + * account the Buffer's current position. This method is only + * meaningful if getBaseArray() returns non-null. * - * @param Buffer b the Buffer to be queried + * @param b the Buffer to be queried * @return the data offset in bytes to the start of this Buffer's data */ static int getBaseArrayOffset(Buffer b) { - return b.hasArray() ? (b.arrayOffset() << b._elementSizeShift) : 0; + return b.hasArray() ? ((b.arrayOffset() + b.position) << b._elementSizeShift) : 0; } } diff --git a/luni/src/main/java/java/nio/PipeImpl.java b/luni/src/main/java/java/nio/PipeImpl.java index 50028f4..d4dde3b 100644 --- a/luni/src/main/java/java/nio/PipeImpl.java +++ b/luni/src/main/java/java/nio/PipeImpl.java @@ -19,8 +19,8 @@ package java.nio; import java.io.Closeable; import java.io.FileDescriptor; import java.io.IOException; -import java.nio.channels.FileChannel; import java.nio.channels.Pipe; +import java.nio.channels.SocketChannel; import java.nio.channels.spi.SelectorProvider; import libcore.io.ErrnoException; import libcore.io.IoUtils; @@ -34,13 +34,16 @@ final class PipeImpl extends Pipe { private final PipeSinkChannel sink; private final PipeSourceChannel source; - public PipeImpl() throws IOException { + public PipeImpl(SelectorProvider selectorProvider) throws IOException { try { - FileDescriptor[] fds = Libcore.os.pipe(); - // Which fd is used for which channel is important. Unix pipes are only guaranteed to be - // unidirectional, and indeed are only unidirectional on Linux. - this.sink = new PipeSinkChannel(fds[1]); - this.source = new PipeSourceChannel(fds[0]); + FileDescriptor fd1 = new FileDescriptor(); + FileDescriptor fd2 = new FileDescriptor(); + Libcore.os.socketpair(AF_UNIX, SOCK_STREAM, 0, fd1, fd2); + + // It doesn't matter which file descriptor we use for which end; + // they're guaranteed to be indistinguishable. + this.sink = new PipeSinkChannel(selectorProvider, fd1); + this.source = new PipeSourceChannel(selectorProvider, fd2); } catch (ErrnoException errnoException) { throw errnoException.rethrowAsIOException(); } @@ -54,27 +57,14 @@ final class PipeImpl extends Pipe { return source; } - /** - * FileChannelImpl doesn't close its fd itself; it calls close on the object it's given. - */ - private static class FdCloser implements Closeable { - private final FileDescriptor fd; - private FdCloser(FileDescriptor fd) { - this.fd = fd; - } - public void close() throws IOException { - IoUtils.close(fd); - } - } - private class PipeSourceChannel extends Pipe.SourceChannel implements FileDescriptorChannel { private final FileDescriptor fd; - private final FileChannel channel; + private final SocketChannel channel; - private PipeSourceChannel(FileDescriptor fd) throws IOException { - super(SelectorProvider.provider()); + private PipeSourceChannel(SelectorProvider selectorProvider, FileDescriptor fd) throws IOException { + super(selectorProvider); this.fd = fd; - this.channel = NioUtils.newFileChannel(new FdCloser(fd), fd, O_RDONLY); + this.channel = new SocketChannelImpl(selectorProvider, fd); } @Override protected void implCloseSelectableChannel() throws IOException { @@ -104,12 +94,12 @@ final class PipeImpl extends Pipe { private class PipeSinkChannel extends Pipe.SinkChannel implements FileDescriptorChannel { private final FileDescriptor fd; - private final FileChannel channel; + private final SocketChannel channel; - private PipeSinkChannel(FileDescriptor fd) throws IOException { - super(SelectorProvider.provider()); + private PipeSinkChannel(SelectorProvider selectorProvider, FileDescriptor fd) throws IOException { + super(selectorProvider); this.fd = fd; - this.channel = NioUtils.newFileChannel(new FdCloser(fd), fd, O_WRONLY); + this.channel = new SocketChannelImpl(selectorProvider, fd); } @Override protected void implCloseSelectableChannel() throws IOException { diff --git a/luni/src/main/java/java/nio/SelectorImpl.java b/luni/src/main/java/java/nio/SelectorImpl.java index 05a6497..02fdf54 100644 --- a/luni/src/main/java/java/nio/SelectorImpl.java +++ b/luni/src/main/java/java/nio/SelectorImpl.java @@ -150,7 +150,7 @@ final class SelectorImpl extends AbstractSelector { @Override public int select(long timeout) throws IOException { if (timeout < 0) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("timeout < 0: " + timeout); } // Our timeout is interpreted differently to Unix's --- 0 means block. See selectNow. return selectInternal((timeout == 0) ? -1 : timeout); diff --git a/luni/src/main/java/java/nio/SelectorProviderImpl.java b/luni/src/main/java/java/nio/SelectorProviderImpl.java index 03003fe..b7c34e8 100644 --- a/luni/src/main/java/java/nio/SelectorProviderImpl.java +++ b/luni/src/main/java/java/nio/SelectorProviderImpl.java @@ -34,7 +34,7 @@ public final class SelectorProviderImpl extends SelectorProvider { } public Pipe openPipe() throws IOException { - return new PipeImpl(); + return new PipeImpl(this); } public AbstractSelector openSelector() throws IOException { diff --git a/luni/src/main/java/java/nio/ShortBuffer.java b/luni/src/main/java/java/nio/ShortBuffer.java index 052cf6b..d12a49e 100644 --- a/luni/src/main/java/java/nio/ShortBuffer.java +++ b/luni/src/main/java/java/nio/ShortBuffer.java @@ -46,7 +46,7 @@ public abstract class ShortBuffer extends Buffer implements */ public static ShortBuffer allocate(int capacity) { if (capacity < 0) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("capacity < 0: " + capacity); } return new ReadWriteShortArrayBuffer(capacity); } @@ -426,7 +426,7 @@ public abstract class ShortBuffer extends Buffer implements */ public ShortBuffer put(ShortBuffer src) { if (src == this) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("src == this"); } if (src.remaining() > remaining()) { throw new BufferOverflowException(); diff --git a/luni/src/main/java/java/nio/SocketChannelImpl.java b/luni/src/main/java/java/nio/SocketChannelImpl.java index 9b26812..233daf0 100644 --- a/luni/src/main/java/java/nio/SocketChannelImpl.java +++ b/luni/src/main/java/java/nio/SocketChannelImpl.java @@ -103,6 +103,15 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel { } /* + * Constructor for use by Pipe.SinkChannel and Pipe.SourceChannel. + */ + public SocketChannelImpl(SelectorProvider selectorProvider, FileDescriptor existingFd) throws IOException { + super(selectorProvider); + status = SOCKET_STATUS_CONNECTED; + fd = existingFd; + } + + /* * Getting the internal Socket If we have not the socket, we create a new * one. */ @@ -318,7 +327,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel { @Override public int write(ByteBuffer src) throws IOException { if (src == null) { - throw new NullPointerException(); + throw new NullPointerException("src == null"); } checkOpenConnected(); if (!src.hasRemaining()) { @@ -412,7 +421,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel { */ static InetSocketAddress validateAddress(SocketAddress socketAddress) { if (socketAddress == null) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("socketAddress == null"); } if (!(socketAddress instanceof InetSocketAddress)) { throw new UnsupportedAddressTypeException(); diff --git a/luni/src/main/java/java/nio/channels/Channels.java b/luni/src/main/java/java/nio/channels/Channels.java index 3af1465..b59eeac 100644 --- a/luni/src/main/java/java/nio/channels/Channels.java +++ b/luni/src/main/java/java/nio/channels/Channels.java @@ -150,7 +150,7 @@ public final class Channels { public static Reader newReader(ReadableByteChannel channel, String charsetName) { if (charsetName == null) { - throw new NullPointerException(); + throw new NullPointerException("charsetName == null"); } return newReader(channel, Charset.forName(charsetName).newDecoder(), -1); } @@ -193,7 +193,7 @@ public final class Channels { public static Writer newWriter(WritableByteChannel channel, String charsetName) { if (charsetName == null) { - throw new NullPointerException(); + throw new NullPointerException("charsetName == null"); } return newWriter(channel, Charset.forName(charsetName).newEncoder(), -1); } @@ -207,7 +207,7 @@ public final class Channels { ChannelInputStream(ReadableByteChannel channel) { if (channel == null) { - throw new NullPointerException(); + throw new NullPointerException("channel == null"); } this.channel = channel; } @@ -247,7 +247,7 @@ public final class Channels { ChannelOutputStream(WritableByteChannel channel) { if (channel == null) { - throw new NullPointerException(); + throw new NullPointerException("channel == null"); } this.channel = channel; } @@ -289,7 +289,7 @@ public final class Channels { InputStreamChannel(InputStream inputStream) { if (inputStream == null) { - throw new NullPointerException(); + throw new NullPointerException("inputStream == null"); } this.inputStream = inputStream; } @@ -328,7 +328,7 @@ public final class Channels { OutputStreamChannel(OutputStream outputStream) { if (outputStream == null) { - throw new NullPointerException(); + throw new NullPointerException("outputStream == null"); } this.outputStream = outputStream; } diff --git a/luni/src/main/java/java/nio/channels/FileLock.java b/luni/src/main/java/java/nio/channels/FileLock.java index 0916be0..4cdcc27 100644 --- a/luni/src/main/java/java/nio/channels/FileLock.java +++ b/luni/src/main/java/java/nio/channels/FileLock.java @@ -98,7 +98,7 @@ public abstract class FileLock { */ protected FileLock(FileChannel channel, long position, long size, boolean shared) { if (position < 0 || size < 0 || position + size < 0) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("position=" + position + " size=" + size); } this.channel = channel; this.position = position; diff --git a/luni/src/main/java/java/nio/charset/CharsetDecoder.java b/luni/src/main/java/java/nio/charset/CharsetDecoder.java index f67dbbc..1559db4 100644 --- a/luni/src/main/java/java/nio/charset/CharsetDecoder.java +++ b/luni/src/main/java/java/nio/charset/CharsetDecoder.java @@ -590,7 +590,7 @@ public abstract class CharsetDecoder { */ public final CharsetDecoder onMalformedInput(CodingErrorAction newAction) { if (newAction == null) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("newAction == null"); } malformedInputAction = newAction; implOnMalformedInput(newAction); @@ -612,7 +612,7 @@ public abstract class CharsetDecoder { */ public final CharsetDecoder onUnmappableCharacter(CodingErrorAction newAction) { if (newAction == null) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("newAction == null"); } unmappableCharacterAction = newAction; implOnUnmappableCharacter(newAction); diff --git a/luni/src/main/java/java/nio/charset/CharsetDecoderICU.java b/luni/src/main/java/java/nio/charset/CharsetDecoderICU.java index 3ad46d7..8b32efa 100644 --- a/luni/src/main/java/java/nio/charset/CharsetDecoderICU.java +++ b/luni/src/main/java/java/nio/charset/CharsetDecoderICU.java @@ -16,7 +16,7 @@ package java.nio.charset; import java.nio.ByteBuffer; import java.nio.CharBuffer; -import libcore.icu.ErrorCode; +import libcore.icu.ICU; import libcore.icu.NativeConverter; import libcore.util.EmptyArray; @@ -46,7 +46,6 @@ final class CharsetDecoderICU extends CharsetDecoder { // is inherently thread-unsafe so we don't have to worry about synchronization. private int inEnd; private int outEnd; - private int ec; public static CharsetDecoderICU newInstance(Charset cs, String icuCanonicalName) { // This complexity is necessary to ensure that even if the constructor, superclass @@ -84,10 +83,7 @@ final class CharsetDecoderICU extends CharsetDecoder { } private void updateCallback() { - ec = NativeConverter.setCallbackDecode(converterHandle, this); - if (ErrorCode.isFailure(ec)) { - throw ErrorCode.throwException(ec); - } + NativeConverter.setCallbackDecode(converterHandle, this); } @Override protected void implReset() { @@ -99,7 +95,6 @@ final class CharsetDecoderICU extends CharsetDecoder { input = null; allocatedInput = null; allocatedOutput = null; - ec = 0; inEnd = 0; outEnd = 0; } @@ -114,16 +109,14 @@ final class CharsetDecoderICU extends CharsetDecoder { data[OUTPUT_OFFSET] = getArray(out); data[INVALID_BYTES] = 0; // Make sure we don't see earlier errors. - ec = NativeConverter.decode(converterHandle, input, inEnd, output, outEnd, data, true); - if (ErrorCode.isFailure(ec)) { - if (ec == ErrorCode.U_BUFFER_OVERFLOW_ERROR) { + int error = NativeConverter.decode(converterHandle, input, inEnd, output, outEnd, data, true); + if (ICU.U_FAILURE(error)) { + if (error == ICU.U_BUFFER_OVERFLOW_ERROR) { return CoderResult.OVERFLOW; - } else if (ec == ErrorCode.U_TRUNCATED_CHAR_FOUND) { + } else if (error == ICU.U_TRUNCATED_CHAR_FOUND) { if (data[INPUT_OFFSET] > 0) { return CoderResult.malformedForLength(data[INPUT_OFFSET]); } - } else { - throw ErrorCode.throwException(ec); } } return CoderResult.UNDERFLOW; @@ -142,13 +135,17 @@ final class CharsetDecoderICU extends CharsetDecoder { data[OUTPUT_OFFSET]= getArray(out); try { - ec = NativeConverter.decode(converterHandle, input, inEnd, output, outEnd, data, false); - if (ec == ErrorCode.U_BUFFER_OVERFLOW_ERROR) { - return CoderResult.OVERFLOW; - } else if (ec == ErrorCode.U_INVALID_CHAR_FOUND) { - return CoderResult.unmappableForLength(data[INVALID_BYTES]); - } else if (ec == ErrorCode.U_ILLEGAL_CHAR_FOUND) { - return CoderResult.malformedForLength(data[INVALID_BYTES]); + int error = NativeConverter.decode(converterHandle, input, inEnd, output, outEnd, data, false); + if (ICU.U_FAILURE(error)) { + if (error == ICU.U_BUFFER_OVERFLOW_ERROR) { + return CoderResult.OVERFLOW; + } else if (error == ICU.U_INVALID_CHAR_FOUND) { + return CoderResult.unmappableForLength(data[INVALID_BYTES]); + } else if (error == ICU.U_ILLEGAL_CHAR_FOUND) { + return CoderResult.malformedForLength(data[INVALID_BYTES]); + } else { + throw new AssertionError(error); + } } // Decoding succeeded: give us more data. return CoderResult.UNDERFLOW; diff --git a/luni/src/main/java/java/nio/charset/CharsetEncoder.java b/luni/src/main/java/java/nio/charset/CharsetEncoder.java index 28b2cb8..8f02f96 100644 --- a/luni/src/main/java/java/nio/charset/CharsetEncoder.java +++ b/luni/src/main/java/java/nio/charset/CharsetEncoder.java @@ -706,11 +706,11 @@ public abstract class CharsetEncoder { throw new IllegalArgumentException("replacement.length == 0"); } if (replacement.length > maxBytesPerChar()) { - throw new IllegalArgumentException("replacement length > maxBytesPerChar: " + + throw new IllegalArgumentException("replacement.length > maxBytesPerChar: " + replacement.length + " > " + maxBytesPerChar()); } if (!isLegalReplacement(replacement)) { - throw new IllegalArgumentException("bad replacement: " + Arrays.toString(replacement)); + throw new IllegalArgumentException("Bad replacement: " + Arrays.toString(replacement)); } // It seems like a bug, but the RI doesn't clone, and we have tests that check we don't. this.replacementBytes = replacement; diff --git a/luni/src/main/java/java/nio/charset/CharsetEncoderICU.java b/luni/src/main/java/java/nio/charset/CharsetEncoderICU.java index cf071ca..76807b0 100644 --- a/luni/src/main/java/java/nio/charset/CharsetEncoderICU.java +++ b/luni/src/main/java/java/nio/charset/CharsetEncoderICU.java @@ -18,7 +18,7 @@ import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.util.HashMap; import java.util.Map; -import libcore.icu.ErrorCode; +import libcore.icu.ICU; import libcore.icu.NativeConverter; import libcore.util.EmptyArray; @@ -61,7 +61,6 @@ final class CharsetEncoderICU extends CharsetEncoder { // is inherently thread-unsafe so we don't have to worry about synchronization. private int inEnd; private int outEnd; - private int ec; public static CharsetEncoderICU newInstance(Charset cs, String icuCanonicalName) { // This complexity is necessary to ensure that even if the constructor, superclass @@ -112,10 +111,7 @@ final class CharsetEncoderICU extends CharsetEncoder { } private void updateCallback() { - ec = NativeConverter.setCallbackEncode(converterHandle, this); - if (ErrorCode.isFailure(ec)) { - throw ErrorCode.throwException(ec); - } + NativeConverter.setCallbackEncode(converterHandle, this); } @Override protected void implReset() { @@ -127,7 +123,6 @@ final class CharsetEncoderICU extends CharsetEncoder { input = null; allocatedInput = null; allocatedOutput = null; - ec = 0; inEnd = 0; outEnd = 0; } @@ -142,16 +137,14 @@ final class CharsetEncoderICU extends CharsetEncoder { data[OUTPUT_OFFSET] = getArray(out); data[INVALID_CHARS] = 0; // Make sure we don't see earlier errors. - ec = NativeConverter.encode(converterHandle, input, inEnd, output, outEnd, data, true); - if (ErrorCode.isFailure(ec)) { - if (ec == ErrorCode.U_BUFFER_OVERFLOW_ERROR) { + int error = NativeConverter.encode(converterHandle, input, inEnd, output, outEnd, data, true); + if (ICU.U_FAILURE(error)) { + if (error == ICU.U_BUFFER_OVERFLOW_ERROR) { return CoderResult.OVERFLOW; - } else if (ec == ErrorCode.U_TRUNCATED_CHAR_FOUND) { + } else if (error == ICU.U_TRUNCATED_CHAR_FOUND) { if (data[INPUT_OFFSET] > 0) { return CoderResult.malformedForLength(data[INPUT_OFFSET]); } - } else { - throw ErrorCode.throwException(ec); } } return CoderResult.UNDERFLOW; @@ -171,16 +164,16 @@ final class CharsetEncoderICU extends CharsetEncoder { data[INVALID_CHARS] = 0; // Make sure we don't see earlier errors. try { - ec = NativeConverter.encode(converterHandle, input, inEnd, output, outEnd, data, false); - if (ErrorCode.isFailure(ec)) { - if (ec == ErrorCode.U_BUFFER_OVERFLOW_ERROR) { + int error = NativeConverter.encode(converterHandle, input, inEnd, output, outEnd, data, false); + if (ICU.U_FAILURE(error)) { + if (error == ICU.U_BUFFER_OVERFLOW_ERROR) { return CoderResult.OVERFLOW; - } else if (ec == ErrorCode.U_INVALID_CHAR_FOUND) { + } else if (error == ICU.U_INVALID_CHAR_FOUND) { return CoderResult.unmappableForLength(data[INVALID_CHARS]); - } else if (ec == ErrorCode.U_ILLEGAL_CHAR_FOUND) { + } else if (error == ICU.U_ILLEGAL_CHAR_FOUND) { return CoderResult.malformedForLength(data[INVALID_CHARS]); } else { - throw new AssertionError("unexpected failure: " + ec); + throw new AssertionError(error); } } // Decoding succeeded: give us more data. diff --git a/luni/src/main/java/java/nio/charset/Charsets.java b/luni/src/main/java/java/nio/charset/Charsets.java index 1c2425a..826b12a 100644 --- a/luni/src/main/java/java/nio/charset/Charsets.java +++ b/luni/src/main/java/java/nio/charset/Charsets.java @@ -25,7 +25,7 @@ package java.nio.charset; * * @hide internal use only */ -public class Charsets { +public final class Charsets { /** * A cheap and type-safe constant for the ISO-8859-1 Charset. */ diff --git a/luni/src/main/java/java/nio/charset/CoderResult.java b/luni/src/main/java/java/nio/charset/CoderResult.java index 221cb32..3cc2673 100644 --- a/luni/src/main/java/java/nio/charset/CoderResult.java +++ b/luni/src/main/java/java/nio/charset/CoderResult.java @@ -121,7 +121,7 @@ public class CoderResult { return r; } } - throw new IllegalArgumentException("Length must be greater than 0; was " + length); + throw new IllegalArgumentException("length <= 0: " + length); } /** @@ -149,7 +149,7 @@ public class CoderResult { return r; } } - throw new IllegalArgumentException("Length must be greater than 0; was " + length); + throw new IllegalArgumentException("length <= 0: " + length); } /** diff --git a/luni/src/main/java/java/security/AlgorithmParameterGenerator.java b/luni/src/main/java/java/security/AlgorithmParameterGenerator.java index daefa3c..3edc167 100644 --- a/luni/src/main/java/java/security/AlgorithmParameterGenerator.java +++ b/luni/src/main/java/java/security/AlgorithmParameterGenerator.java @@ -88,7 +88,7 @@ public class AlgorithmParameterGenerator { public static AlgorithmParameterGenerator getInstance(String algorithm) throws NoSuchAlgorithmException { if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null); return new AlgorithmParameterGenerator((AlgorithmParameterGeneratorSpi) sap.spi, @@ -149,7 +149,7 @@ public class AlgorithmParameterGenerator { throw new IllegalArgumentException(); } if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } Object spi = ENGINE.getInstance(algorithm, provider, null); return new AlgorithmParameterGenerator((AlgorithmParameterGeneratorSpi) spi, provider, diff --git a/luni/src/main/java/java/security/AlgorithmParameters.java b/luni/src/main/java/java/security/AlgorithmParameters.java index 8bbbe1b..073460e 100644 --- a/luni/src/main/java/java/security/AlgorithmParameters.java +++ b/luni/src/main/java/java/security/AlgorithmParameters.java @@ -92,7 +92,7 @@ public class AlgorithmParameters { public static AlgorithmParameters getInstance(String algorithm) throws NoSuchAlgorithmException { if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null); return new AlgorithmParameters((AlgorithmParametersSpi) sap.spi, sap.provider, algorithm); @@ -120,7 +120,7 @@ public class AlgorithmParameters { String provider) throws NoSuchAlgorithmException, NoSuchProviderException { if (provider == null || provider.isEmpty()) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("provider == null || provider.isEmpty()"); } Provider p = Security.getProvider(provider); if (p == null) { @@ -148,10 +148,10 @@ public class AlgorithmParameters { public static AlgorithmParameters getInstance(String algorithm, Provider provider) throws NoSuchAlgorithmException { if (provider == null) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("provider == null"); } if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } Object spi = ENGINE.getInstance(algorithm, provider, null); return new AlgorithmParameters((AlgorithmParametersSpi) spi, provider, algorithm); diff --git a/luni/src/main/java/java/security/KeyFactory.java b/luni/src/main/java/java/security/KeyFactory.java index 554885a..8d39003 100644 --- a/luni/src/main/java/java/security/KeyFactory.java +++ b/luni/src/main/java/java/security/KeyFactory.java @@ -76,7 +76,7 @@ public class KeyFactory { public static KeyFactory getInstance(String algorithm) throws NoSuchAlgorithmException { if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null); return new KeyFactory((KeyFactorySpi) sap.spi, sap.provider, algorithm); @@ -130,7 +130,7 @@ public class KeyFactory { throw new IllegalArgumentException(); } if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } Object spi = ENGINE.getInstance(algorithm, provider, null); return new KeyFactory((KeyFactorySpi) spi, provider, algorithm); diff --git a/luni/src/main/java/java/security/KeyPairGenerator.java b/luni/src/main/java/java/security/KeyPairGenerator.java index d5851fa..5c17d79 100644 --- a/luni/src/main/java/java/security/KeyPairGenerator.java +++ b/luni/src/main/java/java/security/KeyPairGenerator.java @@ -80,7 +80,7 @@ public abstract class KeyPairGenerator extends KeyPairGeneratorSpi { public static KeyPairGenerator getInstance(String algorithm) throws NoSuchAlgorithmException { if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null); Object spi = sap.spi; @@ -143,7 +143,7 @@ public abstract class KeyPairGenerator extends KeyPairGeneratorSpi { throw new IllegalArgumentException(); } if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } Object spi = ENGINE.getInstance(algorithm, provider, null); if (spi instanceof KeyPairGenerator) { diff --git a/luni/src/main/java/java/security/KeyStore.java b/luni/src/main/java/java/security/KeyStore.java index c233a5b..3d856f7 100644 --- a/luni/src/main/java/java/security/KeyStore.java +++ b/luni/src/main/java/java/security/KeyStore.java @@ -110,7 +110,7 @@ public class KeyStore { */ public static KeyStore getInstance(String type) throws KeyStoreException { if (type == null) { - throw new NullPointerException(); + throw new NullPointerException("type == null"); } try { Engine.SpiAndProvider sap = ENGINE.getInstance(type, null); @@ -182,7 +182,7 @@ public class KeyStore { throw new IllegalArgumentException(); } if (type == null) { - throw new NullPointerException(); + throw new NullPointerException("type == null"); } // return KeyStore instance try { diff --git a/luni/src/main/java/java/security/KeyStoreSpi.java b/luni/src/main/java/java/security/KeyStoreSpi.java index 01565f5..5ae9a72 100644 --- a/luni/src/main/java/java/security/KeyStoreSpi.java +++ b/luni/src/main/java/java/security/KeyStoreSpi.java @@ -414,14 +414,14 @@ public abstract class KeyStoreSpi { } char[] passW = null; - if (protParam instanceof KeyStore.PasswordProtection) { - try { - passW = ((KeyStore.PasswordProtection) protParam).getPassword(); - } catch (IllegalStateException ee) { - throw new KeyStoreException("Password was destroyed", ee); - } - } else { - if (protParam instanceof KeyStore.CallbackHandlerProtection) { + if (protParam != null) { + if (protParam instanceof KeyStore.PasswordProtection) { + try { + passW = ((KeyStore.PasswordProtection) protParam).getPassword(); + } catch (IllegalStateException ee) { + throw new KeyStoreException("Password was destroyed", ee); + } + } else if (protParam instanceof KeyStore.CallbackHandlerProtection) { try { passW = getPasswordFromCallBack(protParam); } catch (Exception e) { diff --git a/luni/src/main/java/java/security/MessageDigest.java b/luni/src/main/java/java/security/MessageDigest.java index 3154028..6f1bf21 100644 --- a/luni/src/main/java/java/security/MessageDigest.java +++ b/luni/src/main/java/java/security/MessageDigest.java @@ -86,7 +86,7 @@ public abstract class MessageDigest extends MessageDigestSpi { public static MessageDigest getInstance(String algorithm) throws NoSuchAlgorithmException { if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null); Object spi = sap.spi; @@ -152,7 +152,7 @@ public abstract class MessageDigest extends MessageDigestSpi { throw new IllegalArgumentException(); } if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } Object spi = ENGINE.getInstance(algorithm, provider, null); if (spi instanceof MessageDigest) { @@ -217,7 +217,7 @@ public abstract class MessageDigest extends MessageDigestSpi { */ public void update(byte[] input) { if (input == null) { - throw new NullPointerException(); + throw new NullPointerException("input == null"); } engineUpdate(input, 0, input.length); } diff --git a/luni/src/main/java/java/security/Provider.java b/luni/src/main/java/java/security/Provider.java index 2de3751..899625a 100644 --- a/luni/src/main/java/java/security/Provider.java +++ b/luni/src/main/java/java/security/Provider.java @@ -27,12 +27,13 @@ import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Properties; import java.util.Set; -import org.apache.harmony.luni.util.TwoKeyHashMap; import org.apache.harmony.security.fortress.Services; /** @@ -57,18 +58,18 @@ public abstract class Provider extends Properties { // Contains "Service.Algorithm" and Provider.Service classes added using // putService() - private transient TwoKeyHashMap<String, String, Service> serviceTable; + private transient LinkedHashMap<String, Service> serviceTable; // Contains "Service.Alias" and Provider.Service classes added using // putService() - private transient TwoKeyHashMap<String, String, Service> aliasTable; + private transient LinkedHashMap<String, Service> aliasTable; // Contains "Service.Algorithm" and Provider.Service classes added using // put() - private transient TwoKeyHashMap<String, String, Service> propertyServiceTable; + private transient LinkedHashMap<String, Service> propertyServiceTable; // Contains "Service.Alias" and Provider.Service classes added using put() - private transient TwoKeyHashMap<String, String, Service> propertyAliasTable; + private transient LinkedHashMap<String, Service> propertyAliasTable; // The properties changed via put() private transient Properties changedProperties; @@ -406,30 +407,32 @@ public abstract class Provider extends Properties { */ public synchronized Provider.Service getService(String type, String algorithm) { - if (type == null || algorithm == null) { - throw new NullPointerException(); + if (type == null) { + throw new NullPointerException("type == null"); + } else if (algorithm == null) { + throw new NullPointerException("algorithm == null"); } if (type.equals(lastServiceName) && algorithm.equalsIgnoreCase(lastAlgorithm)) { return returnedService; } - String alg = algorithm.toUpperCase(Locale.US); + String key = key(type, algorithm); Object o = null; if (serviceTable != null) { - o = serviceTable.get(type, alg); + o = serviceTable.get(key); } if (o == null && aliasTable != null) { - o = aliasTable.get(type, alg); + o = aliasTable.get(key); } if (o == null) { updatePropertyServiceTable(); } if (o == null && propertyServiceTable != null) { - o = propertyServiceTable.get(type, alg); + o = propertyServiceTable.get(key); } if (o == null && propertyAliasTable != null) { - o = propertyAliasTable.get(type, alg); + o = propertyAliasTable.get(key); } if (o != null) { @@ -454,9 +457,9 @@ public abstract class Provider extends Properties { return lastServicesSet; } if (serviceTable != null) { - lastServicesSet = new HashSet<Service>(serviceTable.values()); + lastServicesSet = new LinkedHashSet<Service>(serviceTable.values()); } else { - lastServicesSet = new HashSet<Service>(); + lastServicesSet = new LinkedHashSet<Service>(); } if (propertyServiceTable != null) { lastServicesSet.addAll(propertyServiceTable.values()); @@ -474,22 +477,22 @@ public abstract class Provider extends Properties { */ protected synchronized void putService(Provider.Service s) { if (s == null) { - throw new NullPointerException(); + throw new NullPointerException("s == null"); } if ("Provider".equals(s.getType())) { // Provider service type cannot be added return; } servicesChanged(); if (serviceTable == null) { - serviceTable = new TwoKeyHashMap<String, String, Service>(128); + serviceTable = new LinkedHashMap<String, Service>(128); } - serviceTable.put(s.type, s.algorithm.toUpperCase(Locale.US), s); + serviceTable.put(key(s.type, s.algorithm), s); if (s.aliases != null) { if (aliasTable == null) { - aliasTable = new TwoKeyHashMap<String, String, Service>(256); + aliasTable = new LinkedHashMap<String, Service>(256); } for (String alias : s.getAliases()) { - aliasTable.put(s.type, alias.toUpperCase(Locale.US), s); + aliasTable.put(key(s.type, alias), s); } } serviceInfoToProperties(s); @@ -506,15 +509,15 @@ public abstract class Provider extends Properties { */ protected synchronized void removeService(Provider.Service s) { if (s == null) { - throw new NullPointerException(); + throw new NullPointerException("s == null"); } servicesChanged(); if (serviceTable != null) { - serviceTable.remove(s.type, s.algorithm.toUpperCase(Locale.US)); + serviceTable.remove(key(s.type, s.algorithm)); } if (aliasTable != null && s.aliases != null) { for (String alias: s.getAliases()) { - aliasTable.remove(s.type, alias.toUpperCase(Locale.US)); + aliasTable.remove(key(s.type, alias)); } } serviceInfoFromProperties(s); @@ -584,7 +587,7 @@ public abstract class Provider extends Properties { serviceName = service_alias.substring(0, i); aliasName = service_alias.substring(i + 1); if (propertyAliasTable != null) { - propertyAliasTable.remove(serviceName, aliasName.toUpperCase(Locale.US)); + propertyAliasTable.remove(key(serviceName, aliasName)); } if (propertyServiceTable != null) { for (Iterator<Service> it = propertyServiceTable.values().iterator(); it @@ -608,12 +611,11 @@ public abstract class Provider extends Properties { serviceName = k.substring(0, j); algorithm = k.substring(j + 1); if (propertyServiceTable != null) { - Provider.Service ser = propertyServiceTable.remove(serviceName, - algorithm.toUpperCase(Locale.US)); + Provider.Service ser = propertyServiceTable.remove(key(serviceName, algorithm)); if (ser != null && propertyAliasTable != null && ser.aliases != null) { for (String alias : ser.aliases) { - propertyAliasTable.remove(serviceName, alias.toUpperCase(Locale.US)); + propertyAliasTable.remove(key(serviceName, alias)); } } } @@ -624,7 +626,7 @@ public abstract class Provider extends Properties { serviceName = k.substring(0, j); algorithm = k.substring(j + 1, i); if (propertyServiceTable != null) { - Object o = propertyServiceTable.get(serviceName, algorithm.toUpperCase(Locale.US)); + Object o = propertyServiceTable.get(key(serviceName, algorithm)); if (o != null) { s = (Provider.Service) o; s.attributes.remove(attribute); @@ -667,20 +669,20 @@ public abstract class Provider extends Properties { serviceName = service_alias.substring(0, i); aliasName = service_alias.substring(i + 1); algorithm = value; - String algUp = algorithm.toUpperCase(Locale.US); + String propertyServiceTableKey = key(serviceName, algorithm); Object o = null; if (propertyServiceTable == null) { - propertyServiceTable = new TwoKeyHashMap<String, String, Service>(128); + propertyServiceTable = new LinkedHashMap<String, Service>(128); } else { - o = propertyServiceTable.get(serviceName, algUp); + o = propertyServiceTable.get(propertyServiceTableKey); } if (o != null) { s = (Provider.Service) o; s.addAlias(aliasName); if (propertyAliasTable == null) { - propertyAliasTable = new TwoKeyHashMap<String, String, Service>(256); + propertyAliasTable = new LinkedHashMap<String, Service>(256); } - propertyAliasTable.put(serviceName, aliasName.toUpperCase(Locale.US), s); + propertyAliasTable.put(key(serviceName, aliasName), s); } else { String className = (String) changedProperties .get(serviceName + "." + algorithm); @@ -689,11 +691,11 @@ public abstract class Provider extends Properties { l.add(aliasName); s = new Provider.Service(this, serviceName, algorithm, className, l, new HashMap<String, String>()); - propertyServiceTable.put(serviceName, algUp, s); + propertyServiceTable.put(propertyServiceTableKey, s); if (propertyAliasTable == null) { - propertyAliasTable = new TwoKeyHashMap<String, String, Service>(256); + propertyAliasTable = new LinkedHashMap<String, Service>(256); } - propertyAliasTable.put(serviceName, aliasName.toUpperCase(Locale.US), s); + propertyAliasTable.put(key(serviceName, aliasName), s); } } continue; @@ -706,10 +708,10 @@ public abstract class Provider extends Properties { if (i == -1) { // <crypto_service>.<algorithm_or_type>=<className> serviceName = key.substring(0, j); algorithm = key.substring(j + 1); - String alg = algorithm.toUpperCase(Locale.US); + String propertyServiceTableKey = key(serviceName, algorithm); Object o = null; if (propertyServiceTable != null) { - o = propertyServiceTable.get(serviceName, alg); + o = propertyServiceTable.get(propertyServiceTableKey); } if (o != null) { s = (Provider.Service) o; @@ -719,21 +721,20 @@ public abstract class Provider extends Properties { value, Collections.<String>emptyList(), Collections.<String,String>emptyMap()); if (propertyServiceTable == null) { - propertyServiceTable = new TwoKeyHashMap<String, String, Service>(128); + propertyServiceTable = new LinkedHashMap<String, Service>(128); } - propertyServiceTable.put(serviceName, alg, s); + propertyServiceTable.put(propertyServiceTableKey, s); } } else { - // <crypto_service>.<algorithm_or_type> - // <attribute_name>=<attrValue> + // <crypto_service>.<algorithm_or_type> <attribute_name>=<attrValue> serviceName = key.substring(0, j); algorithm = key.substring(j + 1, i); String attribute = key.substring(i + 1); - String alg = algorithm.toUpperCase(Locale.US); + String propertyServiceTableKey = key(serviceName, algorithm); Object o = null; if (propertyServiceTable != null) { - o = propertyServiceTable.get(serviceName, alg); + o = propertyServiceTable.get(propertyServiceTableKey); } if (o != null) { s = (Provider.Service) o; @@ -747,9 +748,9 @@ public abstract class Provider extends Properties { s = new Provider.Service(this, serviceName, algorithm, className, new ArrayList<String>(), m); if (propertyServiceTable == null) { - propertyServiceTable = new TwoKeyHashMap<String, String, Service>(128); + propertyServiceTable = new LinkedHashMap<String, Service>(128); } - propertyServiceTable.put(serviceName, alg, s); + propertyServiceTable.put(propertyServiceTableKey, s); } } } @@ -794,6 +795,10 @@ public abstract class Provider extends Properties { return null; } + private static String key(String type, String algorithm) { + return type + '.' + algorithm.toUpperCase(Locale.US); + } + /** * {@code Service} represents a service in the Java Security infrastructure. * Each service describes its type, the algorithm it implements, to which @@ -849,9 +854,14 @@ public abstract class Provider extends Properties { */ public Service(Provider provider, String type, String algorithm, String className, List<String> aliases, Map<String, String> attributes) { - if (provider == null || type == null || algorithm == null - || className == null) { - throw new NullPointerException(); + if (provider == null) { + throw new NullPointerException("provider == null"); + } else if (type == null) { + throw new NullPointerException("type == null"); + } else if (algorithm == null) { + throw new NullPointerException("algorithm == null"); + } else if (className == null) { + throw new NullPointerException("className == null"); } this.provider = provider; this.type = type; @@ -940,7 +950,7 @@ public abstract class Provider extends Properties { */ public final String getAttribute(String name) { if (name == null) { - throw new NullPointerException(); + throw new NullPointerException("name == null"); } if (attributes == null) { return null; diff --git a/luni/src/main/java/java/security/SecureRandom.java b/luni/src/main/java/java/security/SecureRandom.java index 68a2917..6ed631c 100644 --- a/luni/src/main/java/java/security/SecureRandom.java +++ b/luni/src/main/java/java/security/SecureRandom.java @@ -88,7 +88,6 @@ public class SecureRandom extends Random { */ public SecureRandom() { super(0); - Services.refresh(); Provider.Service service = Services.getSecureRandomService(); if (service == null) { this.provider = null; @@ -154,7 +153,7 @@ public class SecureRandom extends Random { */ public static SecureRandom getInstance(String algorithm) throws NoSuchAlgorithmException { if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null); return new SecureRandom((SecureRandomSpi) sap.spi, sap.provider, @@ -213,7 +212,7 @@ public class SecureRandom extends Random { throw new IllegalArgumentException(); } if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } Object spi = ENGINE.getInstance(algorithm, provider, null); return new SecureRandom((SecureRandomSpi) spi, provider, algorithm); diff --git a/luni/src/main/java/java/security/Security.java b/luni/src/main/java/java/security/Security.java index a4cc6e1..b5bd02a 100644 --- a/luni/src/main/java/java/security/Security.java +++ b/luni/src/main/java/java/security/Security.java @@ -227,7 +227,7 @@ public final class Security { */ public static Provider[] getProviders(String filter) { if (filter == null) { - throw new NullPointerException(); + throw new NullPointerException("filter == null"); } if (filter.length() == 0) { throw new InvalidParameterException(); @@ -271,7 +271,7 @@ public final class Security { */ public static synchronized Provider[] getProviders(Map<String,String> filter) { if (filter == null) { - throw new NullPointerException(); + throw new NullPointerException("filter == null"); } if (filter.isEmpty()) { return null; diff --git a/luni/src/main/java/java/security/Signature.java b/luni/src/main/java/java/security/Signature.java index d9e1e41..be89654 100644 --- a/luni/src/main/java/java/security/Signature.java +++ b/luni/src/main/java/java/security/Signature.java @@ -99,7 +99,7 @@ public abstract class Signature extends SignatureSpi { public static Signature getInstance(String algorithm) throws NoSuchAlgorithmException { if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null); Object spi = sap.spi; @@ -134,7 +134,7 @@ public abstract class Signature extends SignatureSpi { public static Signature getInstance(String algorithm, String provider) throws NoSuchAlgorithmException, NoSuchProviderException { if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } if (provider == null || provider.isEmpty()) { throw new IllegalArgumentException(); @@ -165,7 +165,7 @@ public abstract class Signature extends SignatureSpi { public static Signature getInstance(String algorithm, Provider provider) throws NoSuchAlgorithmException { if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } if (provider == null) { throw new IllegalArgumentException(); diff --git a/luni/src/main/java/java/security/Signer.java b/luni/src/main/java/java/security/Signer.java index 1e4412a..b892090 100644 --- a/luni/src/main/java/java/security/Signer.java +++ b/luni/src/main/java/java/security/Signer.java @@ -83,7 +83,7 @@ public abstract class Signer extends Identity { */ public final void setKeyPair(KeyPair pair) throws InvalidParameterException, KeyException { if (pair == null) { - throw new NullPointerException(); + throw new NullPointerException("pair == null"); } if (pair.getPrivate() == null || pair.getPublic() == null) { diff --git a/luni/src/main/java/java/security/cert/CertPathBuilder.java b/luni/src/main/java/java/security/cert/CertPathBuilder.java index aa65fe7..42029e5 100644 --- a/luni/src/main/java/java/security/cert/CertPathBuilder.java +++ b/luni/src/main/java/java/security/cert/CertPathBuilder.java @@ -102,7 +102,7 @@ public class CertPathBuilder { public static CertPathBuilder getInstance(String algorithm) throws NoSuchAlgorithmException { if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null); return new CertPathBuilder((CertPathBuilderSpi) sap.spi, sap.provider, algorithm); @@ -128,7 +128,7 @@ public class CertPathBuilder { public static CertPathBuilder getInstance(String algorithm, String provider) throws NoSuchAlgorithmException, NoSuchProviderException { if (provider == null || provider.isEmpty()) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("provider == null || provider.isEmpty()"); } Provider impProvider = Security.getProvider(provider); if (impProvider == null) { @@ -156,10 +156,10 @@ public class CertPathBuilder { public static CertPathBuilder getInstance(String algorithm, Provider provider) throws NoSuchAlgorithmException { if (provider == null) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("provider == null"); } if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } Object spi = ENGINE.getInstance(algorithm, provider, null); return new CertPathBuilder((CertPathBuilderSpi) spi, provider, algorithm); diff --git a/luni/src/main/java/java/security/cert/CertPathValidator.java b/luni/src/main/java/java/security/cert/CertPathValidator.java index 69b9f99..ddf78bf 100644 --- a/luni/src/main/java/java/security/cert/CertPathValidator.java +++ b/luni/src/main/java/java/security/cert/CertPathValidator.java @@ -101,7 +101,7 @@ public class CertPathValidator { public static CertPathValidator getInstance(String algorithm) throws NoSuchAlgorithmException { if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null); return new CertPathValidator((CertPathValidatorSpi) sap.spi, sap.provider, algorithm); @@ -160,7 +160,7 @@ public class CertPathValidator { throw new IllegalArgumentException(); } if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } Object spi = ENGINE.getInstance(algorithm, provider, null); return new CertPathValidator((CertPathValidatorSpi) spi, provider, algorithm); diff --git a/luni/src/main/java/java/security/cert/CertStore.java b/luni/src/main/java/java/security/cert/CertStore.java index 6cdaea7..2e28828 100644 --- a/luni/src/main/java/java/security/cert/CertStore.java +++ b/luni/src/main/java/java/security/cert/CertStore.java @@ -97,7 +97,7 @@ public class CertStore { public static CertStore getInstance(String type, CertStoreParameters params) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException { if (type == null) { - throw new NullPointerException(); + throw new NullPointerException("type == null"); } try { Engine.SpiAndProvider sap = ENGINE.getInstance(type, params); @@ -140,7 +140,7 @@ public class CertStore { throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchProviderException { if (provider == null || provider.isEmpty()) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("provider == null || provider.isEmpty()"); } Provider impProvider = Security.getProvider(provider); if (impProvider == null) { @@ -172,10 +172,10 @@ public class CertStore { CertStoreParameters params, Provider provider) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { if (provider == null) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("provider == null"); } if (type == null) { - throw new NullPointerException(); + throw new NullPointerException("type == null"); } try { Object spi = ENGINE.getInstance(type, provider, params); diff --git a/luni/src/main/java/java/security/cert/CertificateFactory.java b/luni/src/main/java/java/security/cert/CertificateFactory.java index 1aac1a0..83d40d3 100644 --- a/luni/src/main/java/java/security/cert/CertificateFactory.java +++ b/luni/src/main/java/java/security/cert/CertificateFactory.java @@ -84,7 +84,7 @@ public class CertificateFactory { public static final CertificateFactory getInstance(String type) throws CertificateException { if (type == null) { - throw new NullPointerException(); + throw new NullPointerException("type == null"); } try { Engine.SpiAndProvider sap = ENGINE.getInstance(type, null); @@ -117,7 +117,7 @@ public class CertificateFactory { String provider) throws CertificateException, NoSuchProviderException { if (provider == null || provider.isEmpty()) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("provider == null || provider.isEmpty()"); } Provider impProvider = Security.getProvider(provider); if (impProvider == null) { @@ -147,10 +147,10 @@ public class CertificateFactory { public static final CertificateFactory getInstance(String type, Provider provider) throws CertificateException { if (provider == null) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("provider == null"); } if (type == null) { - throw new NullPointerException(); + throw new NullPointerException("type == null"); } try { Object spi = ENGINE.getInstance(type, provider, null); diff --git a/luni/src/main/java/java/security/cert/CollectionCertStoreParameters.java b/luni/src/main/java/java/security/cert/CollectionCertStoreParameters.java index de3c85d..9373b40 100644 --- a/luni/src/main/java/java/security/cert/CollectionCertStoreParameters.java +++ b/luni/src/main/java/java/security/cert/CollectionCertStoreParameters.java @@ -58,10 +58,10 @@ public class CollectionCertStoreParameters implements CertStoreParameters { * if {@code collection is null}. */ public CollectionCertStoreParameters(Collection<?> collection) { - this.collection = collection; - if (this.collection == null) { - throw new NullPointerException(); + if (collection == null) { + throw new NullPointerException("collection == null"); } + this.collection = collection; } /** diff --git a/luni/src/main/java/java/security/cert/LDAPCertStoreParameters.java b/luni/src/main/java/java/security/cert/LDAPCertStoreParameters.java index 5b01f90..163c99a 100644 --- a/luni/src/main/java/java/security/cert/LDAPCertStoreParameters.java +++ b/luni/src/main/java/java/security/cert/LDAPCertStoreParameters.java @@ -43,11 +43,11 @@ public class LDAPCertStoreParameters implements CertStoreParameters { * is {@code serverName} is {@code null}. */ public LDAPCertStoreParameters(String serverName, int port) { + if (serverName == null) { + throw new NullPointerException("serverName == null"); + } this.port = port; this.serverName = serverName; - if (this.serverName == null) { - throw new NullPointerException(); - } } /** @@ -71,11 +71,11 @@ public class LDAPCertStoreParameters implements CertStoreParameters { * if {@code serverName} is {@code null}. */ public LDAPCertStoreParameters(String serverName) { + if (serverName == null) { + throw new NullPointerException("serverName == null"); + } this.port = DEFAULT_LDAP_PORT; this.serverName = serverName; - if (this.serverName == null) { - throw new NullPointerException(); - } } /** diff --git a/luni/src/main/java/java/security/cert/PKIXCertPathBuilderResult.java b/luni/src/main/java/java/security/cert/PKIXCertPathBuilderResult.java index 589ce82..2359612 100644 --- a/luni/src/main/java/java/security/cert/PKIXCertPathBuilderResult.java +++ b/luni/src/main/java/java/security/cert/PKIXCertPathBuilderResult.java @@ -49,10 +49,10 @@ public class PKIXCertPathBuilderResult extends PKIXCertPathValidatorResult public PKIXCertPathBuilderResult(CertPath certPath, TrustAnchor trustAnchor, PolicyNode policyTree, PublicKey subjectPublicKey) { super(trustAnchor, policyTree, subjectPublicKey); - this.certPath = certPath; - if (this.certPath == null) { + if (certPath == null) { throw new NullPointerException("certPath == null"); } + this.certPath = certPath; } /** diff --git a/luni/src/main/java/java/security/cert/X509CRL.java b/luni/src/main/java/java/security/cert/X509CRL.java index 4badd59..4addb0e 100644 --- a/luni/src/main/java/java/security/cert/X509CRL.java +++ b/luni/src/main/java/java/security/cert/X509CRL.java @@ -218,7 +218,7 @@ public abstract class X509CRL extends CRL implements X509Extension { */ public X509CRLEntry getRevokedCertificate(X509Certificate certificate) { if (certificate == null) { - throw new NullPointerException(); + throw new NullPointerException("certificate == null"); } return getRevokedCertificate(certificate.getSerialNumber()); } diff --git a/luni/src/main/java/java/security/spec/ECGenParameterSpec.java b/luni/src/main/java/java/security/spec/ECGenParameterSpec.java index fe66b1e..c22038d 100644 --- a/luni/src/main/java/java/security/spec/ECGenParameterSpec.java +++ b/luni/src/main/java/java/security/spec/ECGenParameterSpec.java @@ -35,7 +35,7 @@ public class ECGenParameterSpec implements AlgorithmParameterSpec { public ECGenParameterSpec(String name) { this.name = name; if (this.name == null) { - throw new NullPointerException(); + throw new NullPointerException("name == null"); } } diff --git a/luni/src/main/java/java/sql/DataTruncation.java b/luni/src/main/java/java/sql/DataTruncation.java index b01100e..c1bf11c 100644 --- a/luni/src/main/java/java/sql/DataTruncation.java +++ b/luni/src/main/java/java/sql/DataTruncation.java @@ -49,7 +49,7 @@ public class DataTruncation extends SQLWarning implements Serializable { /** * Creates the {@code DataTruncation} object. The reason is set to {@code - * "Data truncation"}, the {@code ErrorCode} is set to the {@code + * "Data truncation"}, the error code is set to the {@code * SQLException} default value, and the other fields are set to the values * supplied as arguments. * @@ -79,7 +79,7 @@ public class DataTruncation extends SQLWarning implements Serializable { /** * Creates a DataTruncation. The Reason is set to "Data truncation", the - * ErrorCode is set to the SQLException default value and other fields are + * error code is set to the SQLException default value and other fields are * set to the values supplied on this method. * * @param index diff --git a/luni/src/main/java/java/sql/DriverManager.java b/luni/src/main/java/java/sql/DriverManager.java index 4cee1fe..c547585 100644 --- a/luni/src/main/java/java/sql/DriverManager.java +++ b/luni/src/main/java/java/sql/DriverManager.java @@ -329,7 +329,7 @@ public class DriverManager { */ public static void registerDriver(Driver driver) throws SQLException { if (driver == null) { - throw new NullPointerException(); + throw new NullPointerException("driver == null"); } synchronized (theDrivers) { theDrivers.add(driver); diff --git a/luni/src/main/java/java/text/AttributedString.java b/luni/src/main/java/java/text/AttributedString.java index 1335067..d679283 100644 --- a/luni/src/main/java/java/text/AttributedString.java +++ b/luni/src/main/java/java/text/AttributedString.java @@ -519,7 +519,7 @@ public class AttributedString { */ public AttributedString(String value) { if (value == null) { - throw new NullPointerException(); + throw new NullPointerException("value == null"); } text = value; attributeMap = new HashMap<Attribute, List<Range>>(11); @@ -542,7 +542,7 @@ public class AttributedString { public AttributedString(String value, Map<? extends AttributedCharacterIterator.Attribute, ?> attributes) { if (value == null) { - throw new NullPointerException(); + throw new NullPointerException("value == null"); } if (value.length() == 0 && !attributes.isEmpty()) { throw new IllegalArgumentException("Cannot add attributes to empty string"); @@ -575,7 +575,7 @@ public class AttributedString { */ public void addAttribute(AttributedCharacterIterator.Attribute attribute, Object value) { if (attribute == null) { - throw new NullPointerException(); + throw new NullPointerException("attribute == null"); } if (text.length() == 0) { throw new IllegalArgumentException(); @@ -612,7 +612,7 @@ public class AttributedString { public void addAttribute(AttributedCharacterIterator.Attribute attribute, Object value, int start, int end) { if (attribute == null) { - throw new NullPointerException(); + throw new NullPointerException("attribute == null"); } if (start < 0 || end > text.length() || start >= end) { throw new IllegalArgumentException(); diff --git a/luni/src/main/java/java/text/Collator.java b/luni/src/main/java/java/text/Collator.java index 0fa8c71..2ddb516 100644 --- a/luni/src/main/java/java/text/Collator.java +++ b/luni/src/main/java/java/text/Collator.java @@ -286,7 +286,7 @@ public abstract class Collator implements Comparator<Object>, Cloneable { */ public static Collator getInstance(Locale locale) { if (locale == null) { - throw new NullPointerException(); + throw new NullPointerException("locale == null"); } return new RuleBasedCollator(new RuleBasedCollatorICU(locale)); } diff --git a/luni/src/main/java/java/text/DateFormatSymbols.java b/luni/src/main/java/java/text/DateFormatSymbols.java index 3c4768d..e2a2345 100644 --- a/luni/src/main/java/java/text/DateFormatSymbols.java +++ b/luni/src/main/java/java/text/DateFormatSymbols.java @@ -142,7 +142,7 @@ public class DateFormatSymbols implements Serializable, Cloneable { */ public static final DateFormatSymbols getInstance(Locale locale) { if (locale == null) { - throw new NullPointerException(); + throw new NullPointerException("locale == null"); } return new DateFormatSymbols(locale); } @@ -410,7 +410,7 @@ public class DateFormatSymbols implements Serializable, Cloneable { */ public void setLocalPatternChars(String data) { if (data == null) { - throw new NullPointerException(); + throw new NullPointerException("data == null"); } localPatternChars = data; } @@ -471,7 +471,7 @@ public class DateFormatSymbols implements Serializable, Cloneable { */ public void setZoneStrings(String[][] zoneStrings) { if (zoneStrings == null) { - throw new NullPointerException(); + throw new NullPointerException("zoneStrings == null"); } for (String[] row : zoneStrings) { if (row.length < 5) { diff --git a/luni/src/main/java/java/text/DecimalFormat.java b/luni/src/main/java/java/text/DecimalFormat.java index c0d67b8..948bec1 100644 --- a/luni/src/main/java/java/text/DecimalFormat.java +++ b/luni/src/main/java/java/text/DecimalFormat.java @@ -642,7 +642,7 @@ public class DecimalFormat extends NumberFormat { @Override public AttributedCharacterIterator formatToCharacterIterator(Object object) { if (object == null) { - throw new NullPointerException(); + throw new NullPointerException("object == null"); } return ndf.formatToCharacterIterator(object); } @@ -1236,7 +1236,7 @@ public class DecimalFormat extends NumberFormat { */ public void setRoundingMode(RoundingMode roundingMode) { if (roundingMode == null) { - throw new NullPointerException(); + throw new NullPointerException("roundingMode == null"); } this.roundingMode = roundingMode; if (roundingMode != RoundingMode.UNNECESSARY) { // ICU4C doesn't support UNNECESSARY. diff --git a/luni/src/main/java/java/text/DecimalFormatSymbols.java b/luni/src/main/java/java/text/DecimalFormatSymbols.java index 9d2bcc1..708b291 100644 --- a/luni/src/main/java/java/text/DecimalFormatSymbols.java +++ b/luni/src/main/java/java/text/DecimalFormatSymbols.java @@ -127,7 +127,7 @@ public class DecimalFormatSymbols implements Cloneable, Serializable { */ public static DecimalFormatSymbols getInstance(Locale locale) { if (locale == null) { - throw new NullPointerException(); + throw new NullPointerException("locale == null"); } return new DecimalFormatSymbols(locale); } @@ -389,7 +389,7 @@ public class DecimalFormatSymbols implements Cloneable, Serializable { */ public void setCurrency(Currency currency) { if (currency == null) { - throw new NullPointerException(); + throw new NullPointerException("currency == null"); } if (currency == this.currency) { return; @@ -558,7 +558,7 @@ public class DecimalFormatSymbols implements Cloneable, Serializable { */ public void setExponentSeparator(String value) { if (value == null) { - throw new NullPointerException(); + throw new NullPointerException("value == null"); } this.exponentSeparator = value; } diff --git a/luni/src/main/java/java/text/MessageFormat.java b/luni/src/main/java/java/text/MessageFormat.java index a98e4fd..2ab78db 100644 --- a/luni/src/main/java/java/text/MessageFormat.java +++ b/luni/src/main/java/java/text/MessageFormat.java @@ -506,7 +506,7 @@ public class MessageFormat extends Format { @Override public AttributedCharacterIterator formatToCharacterIterator(Object object) { if (object == null) { - throw new NullPointerException(); + throw new NullPointerException("object == null"); } StringBuffer buffer = new StringBuffer(); diff --git a/luni/src/main/java/java/text/NumberFormat.java b/luni/src/main/java/java/text/NumberFormat.java index 54a4849..c285e3d 100644 --- a/luni/src/main/java/java/text/NumberFormat.java +++ b/luni/src/main/java/java/text/NumberFormat.java @@ -566,7 +566,7 @@ public abstract class NumberFormat extends Format { @Override public final Object parseObject(String string, ParsePosition position) { if (position == null) { - throw new NullPointerException("position is null"); + throw new NullPointerException("position == null"); } try { return parse(string, position); diff --git a/luni/src/main/java/java/text/RuleBasedCollator.java b/luni/src/main/java/java/text/RuleBasedCollator.java index 4fd8650..cda06db 100644 --- a/luni/src/main/java/java/text/RuleBasedCollator.java +++ b/luni/src/main/java/java/text/RuleBasedCollator.java @@ -284,7 +284,7 @@ public class RuleBasedCollator extends Collator { */ public RuleBasedCollator(String rules) throws ParseException { if (rules == null) { - throw new NullPointerException(); + throw new NullPointerException("rules == null"); } if (rules.isEmpty()) { throw new ParseException("empty rules", 0); @@ -314,7 +314,7 @@ public class RuleBasedCollator extends Collator { */ public CollationElementIterator getCollationElementIterator(CharacterIterator source) { if (source == null) { - throw new NullPointerException(); + throw new NullPointerException("source == null"); } return new CollationElementIterator(icuColl.getCollationElementIterator(source)); } @@ -328,7 +328,7 @@ public class RuleBasedCollator extends Collator { */ public CollationElementIterator getCollationElementIterator(String source) { if (source == null) { - throw new NullPointerException(); + throw new NullPointerException("source == null"); } return new CollationElementIterator(icuColl.getCollationElementIterator(source)); } @@ -385,8 +385,10 @@ public class RuleBasedCollator extends Collator { */ @Override public int compare(String source, String target) { - if (source == null || target == null) { - throw new NullPointerException(); + if (source == null) { + throw new NullPointerException("source == null"); + } else if (target == null) { + throw new NullPointerException("target == null"); } return icuColl.compare(source, target); } diff --git a/luni/src/main/java/java/text/SimpleDateFormat.java b/luni/src/main/java/java/text/SimpleDateFormat.java index 5cad44b..f682c0b 100644 --- a/luni/src/main/java/java/text/SimpleDateFormat.java +++ b/luni/src/main/java/java/text/SimpleDateFormat.java @@ -468,7 +468,7 @@ public class SimpleDateFormat extends DateFormat { @Override public AttributedCharacterIterator formatToCharacterIterator(Object object) { if (object == null) { - throw new NullPointerException(); + throw new NullPointerException("object == null"); } if (object instanceof Date) { return formatToCharacterIteratorImpl((Date) object); @@ -1067,24 +1067,42 @@ public class SimpleDateFormat extends DateFormat { } private Number parseNumber(int max, String string, ParsePosition position) { - int digit, length = string.length(), result = 0; + int length = string.length(); int index = position.getIndex(); if (max > 0 && max < length - index) { length = index + max; } - while (index < length - && (string.charAt(index) == ' ' || string.charAt(index) == '\t')) { - index++; + while (index < length && (string.charAt(index) == ' ' || string.charAt(index) == '\t')) { + ++index; } if (max == 0) { position.setIndex(index); - return numberFormat.parse(string, position); + Number n = numberFormat.parse(string, position); + // In RTL locales, NumberFormat might have parsed "2012-" in an ISO date as the + // negative number -2012. + // Ideally, we wouldn't have this broken API that exposes a NumberFormat and expects + // us to use it. The next best thing would be a way to ask the NumberFormat to parse + // positive numbers only, but icu4c supports negative (BCE) years. The best we can do + // is try to recognize when icu4c has done this, and undo it. + if (n != null && n.longValue() < 0) { + if (numberFormat instanceof DecimalFormat) { + DecimalFormat df = (DecimalFormat) numberFormat; + char lastChar = string.charAt(position.getIndex() - 1); + char minusSign = df.getDecimalFormatSymbols().getMinusSign(); + if (lastChar == minusSign) { + n = Long.valueOf(-n.longValue()); // Make the value positive. + position.setIndex(position.getIndex() - 1); // Spit out the negative sign. + } + } + } + return n; } - while (index < length - && (digit = Character.digit(string.charAt(index), 10)) != -1) { - index++; + int result = 0; + int digit; + while (index < length && (digit = Character.digit(string.charAt(index), 10)) != -1) { result = result * 10 + digit; + ++index; } if (index == position.getIndex()) { position.setErrorIndex(index); @@ -1171,14 +1189,18 @@ public class SimpleDateFormat extends DateFormat { } int raw = zone.getRawOffset(); if (j == TimeZones.LONG_NAME_DST || j == TimeZones.SHORT_NAME_DST) { - /* - * TODO, http://b/4723412 - * We can't use TimeZone#getDSTSavings here because that - * will return 0 if the zone no longer uses DST. We - * should change this to use TimeZone.getOffset(long), - * which requires the complete date to be parsed first. - */ - raw += 3600000; + // Not all time zones use a one-hour difference, so we need to query + // the TimeZone. (Australia/Lord_Howe is the usual example of this.) + int dstSavings = zone.getDSTSavings(); + // One problem with TimeZone.getDSTSavings is that it will return 0 if the + // time zone has stopped using DST, even if we're parsing a date from + // the past. In that case, assume the default. + if (dstSavings == 0) { + // TODO: we should change this to use TimeZone.getOffset(long), + // but that requires the complete date to be parsed first. + dstSavings = 3600000; + } + raw += dstSavings; } calendar.setTimeZone(new SimpleTimeZone(raw, "")); return offset + element[j].length(); diff --git a/luni/src/main/java/java/util/AbstractQueue.java b/luni/src/main/java/java/util/AbstractQueue.java index d368ac9..47f81fd 100644 --- a/luni/src/main/java/java/util/AbstractQueue.java +++ b/luni/src/main/java/java/util/AbstractQueue.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util; @@ -150,9 +150,9 @@ public abstract class AbstractQueue<E> */ public boolean addAll(Collection<? extends E> c) { if (c == null) - throw new NullPointerException(); + throw new NullPointerException("c == null"); if (c == this) - throw new IllegalArgumentException(); + throw new IllegalArgumentException("c == this"); boolean modified = false; for (E e : c) if (add(e)) diff --git a/luni/src/main/java/java/util/ArrayDeque.java b/luni/src/main/java/java/util/ArrayDeque.java index fafcdb4..5ee3f81 100644 --- a/luni/src/main/java/java/util/ArrayDeque.java +++ b/luni/src/main/java/java/util/ArrayDeque.java @@ -1,6 +1,6 @@ /* * Written by Josh Bloch of Google Inc. and released to the public domain, - * as explained at http://creativecommons.org/licenses/publicdomain. + * as explained at http://creativecommons.org/publicdomain/zero/1.0/. */ package java.util; @@ -9,8 +9,6 @@ package java.util; // removed link to collections framework docs // END android-note -import java.io.*; - /** * Resizable-array implementation of the {@link Deque} interface. Array * deques have no capacity restrictions; they grow as necessary to support @@ -53,7 +51,7 @@ import java.io.*; * @param <E> the type of elements held in this collection */ public class ArrayDeque<E> extends AbstractCollection<E> - implements Deque<E>, Cloneable, Serializable + implements Deque<E>, Cloneable, java.io.Serializable { /** * The array in which the elements of the deque are stored. @@ -65,7 +63,7 @@ public class ArrayDeque<E> extends AbstractCollection<E> * other. We also guarantee that all array cells not holding * deque elements are always null. */ - private transient E[] elements; + private transient Object[] elements; /** * The index of the element at the head of the deque (which is the @@ -109,7 +107,7 @@ public class ArrayDeque<E> extends AbstractCollection<E> if (initialCapacity < 0) // Too many elements, must back off initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements } - elements = (E[]) new Object[initialCapacity]; + elements = new Object[initialCapacity]; } /** @@ -127,7 +125,7 @@ public class ArrayDeque<E> extends AbstractCollection<E> Object[] a = new Object[newCapacity]; System.arraycopy(elements, p, a, 0, r); System.arraycopy(elements, 0, a, r, p); - elements = (E[])a; + elements = a; head = 0; tail = n; } @@ -155,7 +153,7 @@ public class ArrayDeque<E> extends AbstractCollection<E> * sufficient to hold 16 elements. */ public ArrayDeque() { - elements = (E[]) new Object[16]; + elements = new Object[16]; } /** @@ -195,7 +193,7 @@ public class ArrayDeque<E> extends AbstractCollection<E> */ public void addFirst(E e) { if (e == null) - throw new NullPointerException(); + throw new NullPointerException("e == null"); elements[head = (head - 1) & (elements.length - 1)] = e; if (head == tail) doubleCapacity(); @@ -211,7 +209,7 @@ public class ArrayDeque<E> extends AbstractCollection<E> */ public void addLast(E e) { if (e == null) - throw new NullPointerException(); + throw new NullPointerException("e == null"); elements[tail] = e; if ( (tail = (tail + 1) & (elements.length - 1)) == head) doubleCapacity(); @@ -263,7 +261,8 @@ public class ArrayDeque<E> extends AbstractCollection<E> public E pollFirst() { int h = head; - E result = elements[h]; // Element is null if deque empty + @SuppressWarnings("unchecked") E result = (E) elements[h]; + // Element is null if deque empty if (result == null) return null; elements[h] = null; // Must null out slot @@ -273,7 +272,7 @@ public class ArrayDeque<E> extends AbstractCollection<E> public E pollLast() { int t = (tail - 1) & (elements.length - 1); - E result = elements[t]; + @SuppressWarnings("unchecked") E result = (E) elements[t]; if (result == null) return null; elements[t] = null; @@ -285,28 +284,33 @@ public class ArrayDeque<E> extends AbstractCollection<E> * @throws NoSuchElementException {@inheritDoc} */ public E getFirst() { - E x = elements[head]; - if (x == null) + @SuppressWarnings("unchecked") E result = (E) elements[head]; + if (result == null) throw new NoSuchElementException(); - return x; + return result; } /** * @throws NoSuchElementException {@inheritDoc} */ public E getLast() { - E x = elements[(tail - 1) & (elements.length - 1)]; - if (x == null) + @SuppressWarnings("unchecked") + E result = (E) elements[(tail - 1) & (elements.length - 1)]; + if (result == null) throw new NoSuchElementException(); - return x; + return result; } public E peekFirst() { - return elements[head]; // elements[head] is null if deque empty + @SuppressWarnings("unchecked") E result = (E) elements[head]; + // elements[head] is null if deque empty + return result; } public E peekLast() { - return elements[(tail - 1) & (elements.length - 1)]; + @SuppressWarnings("unchecked") + E result = (E) elements[(tail - 1) & (elements.length - 1)]; + return result; } /** @@ -326,7 +330,7 @@ public class ArrayDeque<E> extends AbstractCollection<E> return false; int mask = elements.length - 1; int i = head; - E x; + Object x; while ( (x = elements[i]) != null) { if (o.equals(x)) { delete(i); @@ -354,7 +358,7 @@ public class ArrayDeque<E> extends AbstractCollection<E> return false; int mask = elements.length - 1; int i = (tail - 1) & mask; - E x; + Object x; while ( (x = elements[i]) != null) { if (o.equals(x)) { delete(i); @@ -499,7 +503,7 @@ public class ArrayDeque<E> extends AbstractCollection<E> */ private boolean delete(int i) { checkInvariants(); - final E[] elements = this.elements; + final Object[] elements = this.elements; final int mask = elements.length - 1; final int h = head; final int t = tail; @@ -597,7 +601,7 @@ public class ArrayDeque<E> extends AbstractCollection<E> public E next() { if (cursor == fence) throw new NoSuchElementException(); - E result = elements[cursor]; + @SuppressWarnings("unchecked") E result = (E) elements[cursor]; // This check doesn't catch all possible comodifications, // but does catch the ones that corrupt traversal if (tail != fence || result == null) @@ -636,7 +640,7 @@ public class ArrayDeque<E> extends AbstractCollection<E> if (cursor == fence) throw new NoSuchElementException(); cursor = (cursor - 1) & (elements.length - 1); - E result = elements[cursor]; + @SuppressWarnings("unchecked") E result = (E) elements[cursor]; if (head != fence || result == null) throw new ConcurrentModificationException(); lastRet = cursor; @@ -667,7 +671,7 @@ public class ArrayDeque<E> extends AbstractCollection<E> return false; int mask = elements.length - 1; int i = head; - E x; + Object x; while ( (x = elements[i]) != null) { if (o.equals(x)) return true; @@ -750,8 +754,7 @@ public class ArrayDeque<E> extends AbstractCollection<E> * The following code can be used to dump the deque into a newly * allocated array of <tt>String</tt>: * - * <pre> - * String[] y = x.toArray(new String[0]);</pre> + * <pre> {@code String[] y = x.toArray(new String[0]);}</pre> * * Note that <tt>toArray(new Object[0])</tt> is identical in function to * <tt>toArray()</tt>. @@ -765,6 +768,7 @@ public class ArrayDeque<E> extends AbstractCollection<E> * this deque * @throws NullPointerException if the specified array is null */ + @SuppressWarnings("unchecked") public <T> T[] toArray(T[] a) { int size = size(); if (a.length < size) @@ -785,6 +789,7 @@ public class ArrayDeque<E> extends AbstractCollection<E> */ public ArrayDeque<E> clone() { try { + @SuppressWarnings("unchecked") ArrayDeque<E> result = (ArrayDeque<E>) super.clone(); result.elements = Arrays.copyOf(elements, elements.length); return result; @@ -806,7 +811,8 @@ public class ArrayDeque<E> extends AbstractCollection<E> * followed by all of its elements (each an object reference) in * first-to-last order. */ - private void writeObject(ObjectOutputStream s) throws IOException { + private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException { s.defaultWriteObject(); // Write out size @@ -821,8 +827,8 @@ public class ArrayDeque<E> extends AbstractCollection<E> /** * Deserialize this deque. */ - private void readObject(ObjectInputStream s) - throws IOException, ClassNotFoundException { + private void readObject(java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); // Read in size and allocate array @@ -833,6 +839,6 @@ public class ArrayDeque<E> extends AbstractCollection<E> // Read in all elements in the proper order. for (int i = 0; i < size; i++) - elements[i] = (E)s.readObject(); + elements[i] = s.readObject(); } } diff --git a/luni/src/main/java/java/util/Arrays.java b/luni/src/main/java/java/util/Arrays.java index 9d0f4a4..4a149b7 100644 --- a/luni/src/main/java/java/util/Arrays.java +++ b/luni/src/main/java/java/util/Arrays.java @@ -35,7 +35,7 @@ public class Arrays { ArrayList(E[] storage) { if (storage == null) { - throw new NullPointerException(); + throw new NullPointerException("storage == null"); } a = storage; } @@ -2459,7 +2459,7 @@ public class Arrays { */ public static boolean[] copyOf(boolean[] original, int newLength) { if (newLength < 0) { - throw new NegativeArraySizeException(); + throw new NegativeArraySizeException(Integer.toString(newLength)); } return copyOfRange(original, 0, newLength); } @@ -2478,7 +2478,7 @@ public class Arrays { */ public static byte[] copyOf(byte[] original, int newLength) { if (newLength < 0) { - throw new NegativeArraySizeException(); + throw new NegativeArraySizeException(Integer.toString(newLength)); } return copyOfRange(original, 0, newLength); } @@ -2497,7 +2497,7 @@ public class Arrays { */ public static char[] copyOf(char[] original, int newLength) { if (newLength < 0) { - throw new NegativeArraySizeException(); + throw new NegativeArraySizeException(Integer.toString(newLength)); } return copyOfRange(original, 0, newLength); } @@ -2516,7 +2516,7 @@ public class Arrays { */ public static double[] copyOf(double[] original, int newLength) { if (newLength < 0) { - throw new NegativeArraySizeException(); + throw new NegativeArraySizeException(Integer.toString(newLength)); } return copyOfRange(original, 0, newLength); } @@ -2535,7 +2535,7 @@ public class Arrays { */ public static float[] copyOf(float[] original, int newLength) { if (newLength < 0) { - throw new NegativeArraySizeException(); + throw new NegativeArraySizeException(Integer.toString(newLength)); } return copyOfRange(original, 0, newLength); } @@ -2554,7 +2554,7 @@ public class Arrays { */ public static int[] copyOf(int[] original, int newLength) { if (newLength < 0) { - throw new NegativeArraySizeException(); + throw new NegativeArraySizeException(Integer.toString(newLength)); } return copyOfRange(original, 0, newLength); } @@ -2573,7 +2573,7 @@ public class Arrays { */ public static long[] copyOf(long[] original, int newLength) { if (newLength < 0) { - throw new NegativeArraySizeException(); + throw new NegativeArraySizeException(Integer.toString(newLength)); } return copyOfRange(original, 0, newLength); } @@ -2592,7 +2592,7 @@ public class Arrays { */ public static short[] copyOf(short[] original, int newLength) { if (newLength < 0) { - throw new NegativeArraySizeException(); + throw new NegativeArraySizeException(Integer.toString(newLength)); } return copyOfRange(original, 0, newLength); } @@ -2611,10 +2611,10 @@ public class Arrays { */ public static <T> T[] copyOf(T[] original, int newLength) { if (original == null) { - throw new NullPointerException(); + throw new NullPointerException("original == null"); } if (newLength < 0) { - throw new NegativeArraySizeException(); + throw new NegativeArraySizeException(Integer.toString(newLength)); } return copyOfRange(original, 0, newLength); } @@ -2636,7 +2636,7 @@ public class Arrays { public static <T, U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) { // We use the null pointer check in copyOfRange for exception priority compatibility. if (newLength < 0) { - throw new NegativeArraySizeException(); + throw new NegativeArraySizeException(Integer.toString(newLength)); } return copyOfRange(original, 0, newLength, newType); } diff --git a/luni/src/main/java/java/util/BitSet.java b/luni/src/main/java/java/util/BitSet.java index a4ee4c1..9dfe35e 100644 --- a/luni/src/main/java/java/util/BitSet.java +++ b/luni/src/main/java/java/util/BitSet.java @@ -85,7 +85,7 @@ public class BitSet implements Serializable, Cloneable { */ public BitSet(int bitCount) { if (bitCount < 0) { - throw new NegativeArraySizeException(); + throw new NegativeArraySizeException(Integer.toString(bitCount)); } this.bits = arrayForBits(bitCount); this.longCount = 0; diff --git a/luni/src/main/java/java/util/Calendar.java b/luni/src/main/java/java/util/Calendar.java index bef6e26..81d01fb 100644 --- a/luni/src/main/java/java/util/Calendar.java +++ b/luni/src/main/java/java/util/Calendar.java @@ -877,8 +877,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca return getTimeInMillis() == cal.getTimeInMillis() && isLenient() == cal.isLenient() && getFirstDayOfWeek() == cal.getFirstDayOfWeek() - && getMinimalDaysInFirstWeek() == cal - .getMinimalDaysInFirstWeek() + && getMinimalDaysInFirstWeek() == cal.getMinimalDaysInFirstWeek() && getTimeZone().equals(cal.getTimeZone()); } @@ -903,11 +902,8 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca } /** - * Gets the maximum value of the specified field for the current date. - * - * @param field - * the field. - * @return the maximum value of the specified field. + * Returns the maximum value of the specified field for the current date. + * For example, the maximum number of days in the current month. */ public int getActualMaximum(int field) { int value, next; @@ -1393,7 +1389,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca */ public int compareTo(Calendar anotherCalendar) { if (anotherCalendar == null) { - throw new NullPointerException(); + throw new NullPointerException("anotherCalendar == null"); } long timeInMillis = getTimeInMillis(); long anotherTimeInMillis = anotherCalendar.getTimeInMillis(); diff --git a/luni/src/main/java/java/util/Collections.java b/luni/src/main/java/java/util/Collections.java index b6729b4..d49ca85 100644 --- a/luni/src/main/java/java/util/Collections.java +++ b/luni/src/main/java/java/util/Collections.java @@ -1412,7 +1412,7 @@ public class Collections { @SuppressWarnings("unchecked") public static <T> int binarySearch(List<? extends Comparable<? super T>> list, T object) { if (list == null) { - throw new NullPointerException(); + throw new NullPointerException("list == null"); } if (list.isEmpty()) { return -1; @@ -1916,7 +1916,7 @@ public class Collections { @SuppressWarnings("unchecked") public static void swap(List<?> list, int index1, int index2) { if (list == null) { - throw new NullPointerException(); + throw new NullPointerException("list == null"); } final int size = list.size(); if (index1 < 0 || index1 >= size || index2 < 0 || index2 >= size) { @@ -2174,7 +2174,7 @@ public class Collections { public static <T> Collection<T> synchronizedCollection( Collection<T> collection) { if (collection == null) { - throw new NullPointerException(); + throw new NullPointerException("collection == null"); } return new SynchronizedCollection<T>(collection); } @@ -2189,7 +2189,7 @@ public class Collections { */ public static <T> List<T> synchronizedList(List<T> list) { if (list == null) { - throw new NullPointerException(); + throw new NullPointerException("list == null"); } if (list instanceof RandomAccess) { return new SynchronizedRandomAccessList<T>(list); @@ -2207,7 +2207,7 @@ public class Collections { */ public static <K, V> Map<K, V> synchronizedMap(Map<K, V> map) { if (map == null) { - throw new NullPointerException(); + throw new NullPointerException("map == null"); } return new SynchronizedMap<K, V>(map); } @@ -2222,7 +2222,7 @@ public class Collections { */ public static <E> Set<E> synchronizedSet(Set<E> set) { if (set == null) { - throw new NullPointerException(); + throw new NullPointerException("set == null"); } return new SynchronizedSet<E>(set); } @@ -2238,7 +2238,7 @@ public class Collections { public static <K, V> SortedMap<K, V> synchronizedSortedMap( SortedMap<K, V> map) { if (map == null) { - throw new NullPointerException(); + throw new NullPointerException("map == null"); } return new SynchronizedSortedMap<K, V>(map); } @@ -2253,7 +2253,7 @@ public class Collections { */ public static <E> SortedSet<E> synchronizedSortedSet(SortedSet<E> set) { if (set == null) { - throw new NullPointerException(); + throw new NullPointerException("set == null"); } return new SynchronizedSortedSet<E>(set); } @@ -2271,7 +2271,7 @@ public class Collections { public static <E> Collection<E> unmodifiableCollection( Collection<? extends E> collection) { if (collection == null) { - throw new NullPointerException(); + throw new NullPointerException("collection == null"); } return new UnmodifiableCollection<E>((Collection<E>) collection); } @@ -2288,7 +2288,7 @@ public class Collections { @SuppressWarnings("unchecked") public static <E> List<E> unmodifiableList(List<? extends E> list) { if (list == null) { - throw new NullPointerException(); + throw new NullPointerException("list == null"); } if (list instanceof RandomAccess) { return new UnmodifiableRandomAccessList<E>((List<E>) list); @@ -2309,7 +2309,7 @@ public class Collections { public static <K, V> Map<K, V> unmodifiableMap( Map<? extends K, ? extends V> map) { if (map == null) { - throw new NullPointerException(); + throw new NullPointerException("map == null"); } return new UnmodifiableMap<K, V>((Map<K, V>) map); } @@ -2326,7 +2326,7 @@ public class Collections { @SuppressWarnings("unchecked") public static <E> Set<E> unmodifiableSet(Set<? extends E> set) { if (set == null) { - throw new NullPointerException(); + throw new NullPointerException("set == null"); } return new UnmodifiableSet<E>((Set<E>) set); } @@ -2344,7 +2344,7 @@ public class Collections { public static <K, V> SortedMap<K, V> unmodifiableSortedMap( SortedMap<K, ? extends V> map) { if (map == null) { - throw new NullPointerException(); + throw new NullPointerException("map == null"); } return new UnmodifiableSortedMap<K, V>((SortedMap<K, V>) map); } @@ -2360,7 +2360,7 @@ public class Collections { */ public static <E> SortedSet<E> unmodifiableSortedSet(SortedSet<E> set) { if (set == null) { - throw new NullPointerException(); + throw new NullPointerException("set == null"); } return new UnmodifiableSortedSet<E>(set); } @@ -2381,7 +2381,7 @@ public class Collections { */ public static int frequency(Collection<?> c, Object o) { if (c == null) { - throw new NullPointerException(); + throw new NullPointerException("c == null"); } if (c.isEmpty()) { return 0; @@ -2834,8 +2834,10 @@ public class Collections { Class<E> type; public CheckedCollection(Collection<E> c, Class<E> type) { - if (c == null || type == null) { - throw new NullPointerException(); + if (c == null) { + throw new NullPointerException("c == null"); + } else if (type == null) { + throw new NullPointerException("type == null"); } this.c = c; this.type = type; @@ -3079,8 +3081,12 @@ public class Collections { Class<V> valueType; private CheckedMap(Map<K, V> m, Class<K> keyType, Class<V> valueType) { - if (m == null || keyType == null || valueType == null) { - throw new NullPointerException(); + if (m == null) { + throw new NullPointerException("m == null"); + } else if (keyType == null) { + throw new NullPointerException("keyType == null"); + } else if (valueType == null) { + throw new NullPointerException("valueType == null"); } this.m = m; this.keyType = keyType; @@ -3172,7 +3178,7 @@ public class Collections { public CheckedEntry(Map.Entry<K, V> e, Class<V> valueType) { if (e == null) { - throw new NullPointerException(); + throw new NullPointerException("e == null"); } this.e = e; this.valueType = valueType; diff --git a/luni/src/main/java/java/util/Deque.java b/luni/src/main/java/java/util/Deque.java index cb6bd90..f74a6b4 100644 --- a/luni/src/main/java/java/util/Deque.java +++ b/luni/src/main/java/java/util/Deque.java @@ -1,14 +1,13 @@ /* * Written by Doug Lea and Josh Bloch with assistance from members of * JCP JSR-166 Expert Group and released to the public domain, as explained - * at http://creativecommons.org/licenses/publicdomain + * at http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util; // BEGIN android-note // removed link to collections framework docs -// changed {@link #offer(Object)} to {@link #offer} to satisfy DroidDoc // END android-note /** @@ -356,7 +355,7 @@ public interface Deque<E> extends Queue<E> { * <tt>true</tt> upon success and throwing an * <tt>IllegalStateException</tt> if no space is currently available. * When using a capacity-restricted deque, it is generally preferable to - * use {@link #offer offer}. + * use {@link #offer(Object) offer}. * * <p>This method is equivalent to {@link #addLast}. * diff --git a/luni/src/main/java/java/util/DualPivotQuicksort.java b/luni/src/main/java/java/util/DualPivotQuicksort.java index 97797d1..5d2f77f 100644 --- a/luni/src/main/java/java/util/DualPivotQuicksort.java +++ b/luni/src/main/java/java/util/DualPivotQuicksort.java @@ -1565,7 +1565,7 @@ final class DualPivotQuicksort { for (int k = left; k <= n; k++) { float ak = a[k]; - if (ak == 0.0f && NEGATIVE_ZERO == Float.floatToIntBits(ak)) { + if (ak == 0.0f && NEGATIVE_ZERO == Float.floatToRawIntBits(ak)) { a[k] = 0.0f; numNegativeZeros++; } else if (ak != ak) { // i.e., ak is NaN @@ -1938,7 +1938,7 @@ final class DualPivotQuicksort { for (int k = left; k <= n; k++) { double ak = a[k]; - if (ak == 0.0d && NEGATIVE_ZERO == Double.doubleToLongBits(ak)) { + if (ak == 0.0d && NEGATIVE_ZERO == Double.doubleToRawLongBits(ak)) { a[k] = 0.0d; numNegativeZeros++; } else if (ak != ak) { // i.e., ak is NaN diff --git a/luni/src/main/java/java/util/DuplicateFormatFlagsException.java b/luni/src/main/java/java/util/DuplicateFormatFlagsException.java index d04db8e..2a2bc2e 100644 --- a/luni/src/main/java/java/util/DuplicateFormatFlagsException.java +++ b/luni/src/main/java/java/util/DuplicateFormatFlagsException.java @@ -37,7 +37,7 @@ public class DuplicateFormatFlagsException extends IllegalFormatException { */ public DuplicateFormatFlagsException(String f) { if (f == null) { - throw new NullPointerException(); + throw new NullPointerException("f == null"); } flags = f; } diff --git a/luni/src/main/java/java/util/EnumMap.java b/luni/src/main/java/java/util/EnumMap.java index d3d42b4..a721ee3 100644 --- a/luni/src/main/java/java/util/EnumMap.java +++ b/luni/src/main/java/java/util/EnumMap.java @@ -773,7 +773,7 @@ public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements @SuppressWarnings("unchecked") private V putImpl(K key, V value) { if (key == null) { - throw new NullPointerException(); + throw new NullPointerException("key == null"); } keyType.cast(key); // Called to throw ClassCastException. int keyOrdinal = key.ordinal(); diff --git a/luni/src/main/java/java/util/FormatFlagsConversionMismatchException.java b/luni/src/main/java/java/util/FormatFlagsConversionMismatchException.java index 5792877..5c36788 100644 --- a/luni/src/main/java/java/util/FormatFlagsConversionMismatchException.java +++ b/luni/src/main/java/java/util/FormatFlagsConversionMismatchException.java @@ -44,7 +44,7 @@ public class FormatFlagsConversionMismatchException extends */ public FormatFlagsConversionMismatchException(String f, char c) { if (f == null) { - throw new NullPointerException(); + throw new NullPointerException("f == null"); } this.f = f; this.c = c; diff --git a/luni/src/main/java/java/util/Formatter.java b/luni/src/main/java/java/util/Formatter.java index e9a2f4a..021da08 100644 --- a/luni/src/main/java/java/util/Formatter.java +++ b/luni/src/main/java/java/util/Formatter.java @@ -884,7 +884,7 @@ public final class Formatter implements Closeable, Flushable { */ public Formatter(PrintStream ps) { if (ps == null) { - throw new NullPointerException(); + throw new NullPointerException("ps == null"); } out = ps; locale = Locale.getDefault(); diff --git a/luni/src/main/java/java/util/GregorianCalendar.java b/luni/src/main/java/java/util/GregorianCalendar.java index c0fd521..9ff9ccc 100644 --- a/luni/src/main/java/java/util/GregorianCalendar.java +++ b/luni/src/main/java/java/util/GregorianCalendar.java @@ -219,14 +219,6 @@ public class GregorianCalendar extends Calendar { private static int[] leastMaximums = new int[] { 1, 292269054, 11, 50, 3, 28, 355, 7, 3, 1, 11, 23, 59, 59, 999, 50400000, 1200000 }; - private boolean isCached; - - private int[] cachedFields = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - - private long nextMidnightMillis = 0L; - - private long lastMidnightMillis = 0L; - private int currentYearSkew = 10; private int lastYearSkew = 0; @@ -365,8 +357,6 @@ public class GregorianCalendar extends Calendar { throw new IllegalArgumentException(); } - isCached = false; - if (field == ERA) { complete(); if (fields[ERA] == AD) { @@ -468,19 +458,8 @@ public class GregorianCalendar extends Calendar { complete(); } - /** - * Creates new instance of {@code GregorianCalendar} with the same properties. - * - * @return a shallow copy of this {@code GregorianCalendar}. - */ - @Override - public Object clone() { - GregorianCalendar thisClone = (GregorianCalendar) super.clone(); - thisClone.cachedFields = cachedFields.clone(); - return thisClone; - } - - private final void fullFieldsCalc(long timeVal, int millis, int zoneOffset) { + private void fullFieldsCalc(long timeVal, int zoneOffset) { + int millis = (int) (time % 86400000); long days = timeVal / 86400000; if (millis < 0) { @@ -583,31 +562,6 @@ public class GregorianCalendar extends Calendar { } } - private final void cachedFieldsCheckAndGet(long timeVal, - long newTimeMillis, long newTimeMillisAdjusted, int millis, - int zoneOffset) { - int dstOffset = fields[DST_OFFSET]; - if (!isCached - || newTimeMillis >= nextMidnightMillis - || newTimeMillis <= lastMidnightMillis - || cachedFields[4] != zoneOffset - || (dstOffset == 0 && (newTimeMillisAdjusted >= nextMidnightMillis)) - || (dstOffset != 0 && (newTimeMillisAdjusted <= lastMidnightMillis))) { - fullFieldsCalc(timeVal, millis, zoneOffset); - isCached = false; - } else { - fields[YEAR] = cachedFields[0]; - fields[MONTH] = cachedFields[1]; - fields[DATE] = cachedFields[2]; - fields[DAY_OF_WEEK] = cachedFields[3]; - fields[ERA] = cachedFields[5]; - fields[WEEK_OF_YEAR] = cachedFields[6]; - fields[WEEK_OF_MONTH] = cachedFields[7]; - fields[DAY_OF_YEAR] = cachedFields[8]; - fields[DAY_OF_WEEK_IN_MONTH] = cachedFields[9]; - } - } - @Override protected void computeFields() { TimeZone timeZone = getTimeZone(); @@ -616,99 +570,11 @@ public class GregorianCalendar extends Calendar { fields[DST_OFFSET] = dstOffset; fields[ZONE_OFFSET] = zoneOffset; - int millis = (int) (time % 86400000); - int savedMillis = millis; - // compute without a change in daylight saving time - int offset = zoneOffset + dstOffset; - long newTime = time + offset; - - if (time > 0L && newTime < 0L && offset > 0) { - newTime = 0x7fffffffffffffffL; - } else if (time < 0L && newTime > 0L && offset < 0) { - newTime = 0x8000000000000000L; - } - - // FIXME: I don't think this caching ever really gets used, because it requires that the - // time zone doesn't use daylight savings (ever). So unless you're somewhere like Taiwan... - if (isCached) { - if (millis < 0) { - millis += 86400000; - } - - // Cannot add ZONE_OFFSET to time as it might overflow - millis += zoneOffset; - millis += dstOffset; - - if (millis < 0) { - millis += 86400000; - } else if (millis >= 86400000) { - millis -= 86400000; - } - - fields[MILLISECOND] = (millis % 1000); - millis /= 1000; - fields[SECOND] = (millis % 60); - millis /= 60; - fields[MINUTE] = (millis % 60); - millis /= 60; - fields[HOUR_OF_DAY] = (millis % 24); - millis /= 24; - fields[AM_PM] = fields[HOUR_OF_DAY] > 11 ? 1 : 0; - fields[HOUR] = fields[HOUR_OF_DAY] % 12; - - // FIXME: this has to be wrong; useDaylightTime doesn't mean what they think it means! - long newTimeAdjusted = newTime; - if (timeZone.useDaylightTime()) { - int dstSavings = timeZone.getDSTSavings(); - newTimeAdjusted += (dstOffset == 0) ? dstSavings : -dstSavings; - } - - if (newTime > 0L && newTimeAdjusted < 0L && dstOffset == 0) { - newTimeAdjusted = 0x7fffffffffffffffL; - } else if (newTime < 0L && newTimeAdjusted > 0L && dstOffset != 0) { - newTimeAdjusted = 0x8000000000000000L; - } - - cachedFieldsCheckAndGet(time, newTime, newTimeAdjusted, - savedMillis, zoneOffset); - } else { - fullFieldsCalc(time, savedMillis, zoneOffset); - } + fullFieldsCalc(time, zoneOffset); for (int i = 0; i < FIELD_COUNT; i++) { isSet[i] = true; } - - // Caching - if (!isCached - && newTime != 0x7fffffffffffffffL - && newTime != 0x8000000000000000L - && (!timeZone.useDaylightTime() || timeZone instanceof SimpleTimeZone)) { - int cacheMillis = 0; - - cachedFields[0] = fields[YEAR]; - cachedFields[1] = fields[MONTH]; - cachedFields[2] = fields[DATE]; - cachedFields[3] = fields[DAY_OF_WEEK]; - cachedFields[4] = zoneOffset; - cachedFields[5] = fields[ERA]; - cachedFields[6] = fields[WEEK_OF_YEAR]; - cachedFields[7] = fields[WEEK_OF_MONTH]; - cachedFields[8] = fields[DAY_OF_YEAR]; - cachedFields[9] = fields[DAY_OF_WEEK_IN_MONTH]; - - cacheMillis += (23 - fields[HOUR_OF_DAY]) * 60 * 60 * 1000; - cacheMillis += (59 - fields[MINUTE]) * 60 * 1000; - cacheMillis += (59 - fields[SECOND]) * 1000; - nextMidnightMillis = newTime + cacheMillis; - - cacheMillis = fields[HOUR_OF_DAY] * 60 * 60 * 1000; - cacheMillis += fields[MINUTE] * 60 * 1000; - cacheMillis += fields[SECOND] * 1000; - lastMidnightMillis = newTime - cacheMillis; - - isCached = true; - } } @Override @@ -939,19 +805,17 @@ public class GregorianCalendar extends Calendar { return (int) days + 1; } - private long daysFromBaseYear(int iyear) { - long year = iyear; - + private long daysFromBaseYear(long year) { if (year >= 1970) { long days = (year - 1970) * 365 + ((year - 1969) / 4); if (year > changeYear) { days -= ((year - 1901) / 100) - ((year - 1601) / 400); } else { - if(year == changeYear){ + if (year == changeYear) { days += currentYearSkew; - }else if(year == changeYear -1){ + } else if (year == changeYear - 1) { days += lastYearSkew; - }else{ + } else { days += julianSkew; } } @@ -995,21 +859,10 @@ public class GregorianCalendar extends Calendar { } /** - * Compares the specified {@code Object} to this {@code GregorianCalendar} and returns whether - * they are equal. To be equal, the {@code Object} must be an instance of {@code GregorianCalendar} and - * have the same properties. - * - * @param object - * the {@code Object} to compare with this {@code GregorianCalendar}. - * @return {@code true} if {@code object} is equal to this - * {@code GregorianCalendar}, {@code false} otherwise. - * @throws IllegalArgumentException - * if the time is not set and the time cannot be computed - * from the current field values. - * @see #hashCode + * Returns true if {@code object} is a GregorianCalendar with the same + * properties. */ - @Override - public boolean equals(Object object) { + @Override public boolean equals(Object object) { if (!(object instanceof GregorianCalendar)) { return false; } @@ -1020,28 +873,12 @@ public class GregorianCalendar extends Calendar { && gregorianCutover == ((GregorianCalendar) object).gregorianCutover; } - /** - * Gets the maximum value of the specified field for the current date. For - * example, the maximum number of days in the current month. - * - * @param field - * the field. - * @return the maximum value of the specified field. - */ - @Override - public int getActualMaximum(int field) { + @Override public int getActualMaximum(int field) { int value; if ((value = maximums[field]) == leastMaximums[field]) { return value; } - switch (field) { - case WEEK_OF_YEAR: - case WEEK_OF_MONTH: - isCached = false; - break; - } - complete(); long orgTime = time; int result = 0; @@ -1216,32 +1053,16 @@ public class GregorianCalendar extends Calendar { month++; } int dayOfWeek = mod7(dayCount - 3) + 1; - int offset = timeZone.getOffset(AD, year, month, date, dayOfWeek, - millis); - return offset; + return timeZone.getOffset(AD, year, month, date, dayOfWeek, millis); } - /** - * Returns an integer hash code for the receiver. Objects which are equal - * return the same value for this method. - * - * @return the receiver's hash. - * - * @see #equals - */ - @Override - public int hashCode() { + @Override public int hashCode() { return super.hashCode() + ((int) (gregorianCutover >>> 32) ^ (int) gregorianCutover); } /** - * Returns whether the specified year is a leap year. - * - * @param year - * the year. - * @return {@code true} if the specified year is a leap year, {@code false} - * otherwise. + * Returns true if {@code year} is a leap year. */ public boolean isLeapYear(int year) { if (year > changeYear) { @@ -1294,8 +1115,6 @@ public class GregorianCalendar extends Calendar { throw new IllegalArgumentException(); } - isCached = false; - complete(); int days, day, mod, maxWeeks, newWeek; int max = -1; @@ -1410,13 +1229,10 @@ public class GregorianCalendar extends Calendar { /** * Sets the gregorian change date of this calendar. - * - * @param date - * a {@code Date} which represents the gregorian change date. */ public void setGregorianChange(Date date) { gregorianCutover = date.getTime(); - GregorianCalendar cal = new GregorianCalendar(TimeZone.GMT); + GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT")); cal.setTime(date); changeYear = cal.get(YEAR); if (cal.get(ERA) == BC) { @@ -1424,7 +1240,6 @@ public class GregorianCalendar extends Calendar { } julianSkew = ((changeYear - 2000) / 400) + julianError() - ((changeYear - 2000) / 100); - isCached = false; int dayOfYear = cal.get(DAY_OF_YEAR); if (dayOfYear < julianSkew) { currentYearSkew = dayOfYear-1; @@ -1433,30 +1248,14 @@ public class GregorianCalendar extends Calendar { lastYearSkew = 0; currentYearSkew = julianSkew; } - isCached = false; } private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); } - private void readObject(ObjectInputStream stream) throws IOException, - ClassNotFoundException { - + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); setGregorianChange(new Date(gregorianCutover)); - isCached = false; - } - - @Override - public void setFirstDayOfWeek(int value) { - super.setFirstDayOfWeek(value); - isCached = false; - } - - @Override - public void setMinimalDaysInFirstWeek(int value) { - super.setMinimalDaysInFirstWeek(value); - isCached = false; } } diff --git a/luni/src/main/java/java/util/Hashtable.java b/luni/src/main/java/java/util/Hashtable.java index cea29da..a4e24bc 100644 --- a/luni/src/main/java/java/util/Hashtable.java +++ b/luni/src/main/java/java/util/Hashtable.java @@ -313,7 +313,7 @@ public class Hashtable<K, V> extends Dictionary<K, V> */ public synchronized boolean containsValue(Object value) { if (value == null) { - throw new NullPointerException(); + throw new NullPointerException("value == null"); } HashtableEntry[] tab = table; @@ -361,8 +361,10 @@ public class Hashtable<K, V> extends Dictionary<K, V> * @see java.lang.Object#equals */ public synchronized V put(K key, V value) { - if (value == null) { - throw new NullPointerException(); + if (key == null) { + throw new NullPointerException("key == null"); + } else if (value == null) { + throw new NullPointerException("value == null"); } int hash = secondaryHash(key.hashCode()); HashtableEntry<K, V>[] tab = table; @@ -395,8 +397,10 @@ public class Hashtable<K, V> extends Dictionary<K, V> * ensure that capacity is sufficient, and does not increment modCount. */ private void constructorPut(K key, V value) { - if (value == null) { - throw new NullPointerException(); + if (key == null) { + throw new NullPointerException("key == null"); + } else if (value == null) { + throw new NullPointerException("value == null"); } int hash = secondaryHash(key.hashCode()); HashtableEntry<K, V>[] tab = table; @@ -680,7 +684,7 @@ public class Hashtable<K, V> extends Dictionary<K, V> public final V setValue(V value) { if (value == null) { - throw new NullPointerException(); + throw new NullPointerException("value == null"); } V oldValue = this.value; this.value = value; diff --git a/luni/src/main/java/java/util/IllegalFormatConversionException.java b/luni/src/main/java/java/util/IllegalFormatConversionException.java index 31c0eed..af986f6 100644 --- a/luni/src/main/java/java/util/IllegalFormatConversionException.java +++ b/luni/src/main/java/java/util/IllegalFormatConversionException.java @@ -46,7 +46,7 @@ public class IllegalFormatConversionException extends IllegalFormatException public IllegalFormatConversionException(char c, Class<?> arg) { this.c = c; if (arg == null) { - throw new NullPointerException(); + throw new NullPointerException("arg == null"); } this.arg = arg; } diff --git a/luni/src/main/java/java/util/IllegalFormatFlagsException.java b/luni/src/main/java/java/util/IllegalFormatFlagsException.java index 6947912..4946c55 100644 --- a/luni/src/main/java/java/util/IllegalFormatFlagsException.java +++ b/luni/src/main/java/java/util/IllegalFormatFlagsException.java @@ -38,7 +38,7 @@ public class IllegalFormatFlagsException extends IllegalFormatException implemen */ public IllegalFormatFlagsException(String flags) { if (flags == null) { - throw new NullPointerException(); + throw new NullPointerException("flags == null"); } this.flags = flags; } diff --git a/luni/src/main/java/java/util/ListResourceBundle.java b/luni/src/main/java/java/util/ListResourceBundle.java index 1508b93..fc6ab97 100644 --- a/luni/src/main/java/java/util/ListResourceBundle.java +++ b/luni/src/main/java/java/util/ListResourceBundle.java @@ -108,7 +108,7 @@ public abstract class ListResourceBundle extends ResourceBundle { public final Object handleGetObject(String key) { initializeTable(); if (key == null) { - throw new NullPointerException(); + throw new NullPointerException("key == null"); } return table.get(key); } diff --git a/luni/src/main/java/java/util/Locale.java b/luni/src/main/java/java/util/Locale.java index 6b20a1c..0fbe2f5 100644 --- a/luni/src/main/java/java/util/Locale.java +++ b/luni/src/main/java/java/util/Locale.java @@ -396,6 +396,17 @@ public final class Locale implements Cloneable, Serializable { if (languageCode.isEmpty()) { return ""; } + + // Last-minute workaround for http://b/7291355 in jb-mr1. + // This isn't right for all languages, but it's right for en and tl. + // We should have more CLDR data in a future release, but we'll still + // probably want to have frameworks/base translate the obsolete tl and + // tl-rPH locales to fil and fil-rPH at runtime, at which point + // libcore and icu4c will just do the right thing. + if (languageCode.equals("tl")) { + return "Filipino"; + } + String result = ICU.getDisplayLanguageNative(toString(), locale.toString()); if (result == null) { // TODO: do we need to do this, or does ICU do it for us? result = ICU.getDisplayLanguageNative(toString(), Locale.getDefault().toString()); diff --git a/luni/src/main/java/java/util/MissingFormatArgumentException.java b/luni/src/main/java/java/util/MissingFormatArgumentException.java index ce72efa..1733501 100644 --- a/luni/src/main/java/java/util/MissingFormatArgumentException.java +++ b/luni/src/main/java/java/util/MissingFormatArgumentException.java @@ -37,7 +37,7 @@ public class MissingFormatArgumentException extends IllegalFormatException { */ public MissingFormatArgumentException(String s) { if (s == null) { - throw new NullPointerException(); + throw new NullPointerException("s == null"); } this.s = s; } diff --git a/luni/src/main/java/java/util/MissingFormatWidthException.java b/luni/src/main/java/java/util/MissingFormatWidthException.java index b6d0ca6..0a3b5ae 100644 --- a/luni/src/main/java/java/util/MissingFormatWidthException.java +++ b/luni/src/main/java/java/util/MissingFormatWidthException.java @@ -36,7 +36,7 @@ public class MissingFormatWidthException extends IllegalFormatException { */ public MissingFormatWidthException(String s) { if (s == null) { - throw new NullPointerException(); + throw new NullPointerException("s == null"); } this.s = s; } diff --git a/luni/src/main/java/java/util/NavigableMap.java b/luni/src/main/java/java/util/NavigableMap.java index 29961c8..beeb651 100644 --- a/luni/src/main/java/java/util/NavigableMap.java +++ b/luni/src/main/java/java/util/NavigableMap.java @@ -1,14 +1,13 @@ /* * Written by Doug Lea and Josh Bloch with assistance from members of JCP * JSR-166 Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util; // BEGIN android-note // removed link to collections framework docs -// changed {@link #subMap(Object)} to {@link #subMap} to satisfy DroidDoc // END android-note /** @@ -48,9 +47,9 @@ package java.util; * method {@code put}. * * <p>Methods - * {@link #subMap subMap(K, K)}, - * {@link #headMap headMap(K)}, and - * {@link #tailMap tailMap(K)} + * {@link #subMap(Object, Object) subMap(K, K)}, + * {@link #headMap(Object) headMap(K)}, and + * {@link #tailMap(Object) tailMap(K)} * are specified to return {@code SortedMap} to allow existing * implementations of {@code SortedMap} to be compatibly retrofitted to * implement {@code NavigableMap}, but extensions and implementations diff --git a/luni/src/main/java/java/util/NavigableSet.java b/luni/src/main/java/java/util/NavigableSet.java index cff0800..f410313 100644 --- a/luni/src/main/java/java/util/NavigableSet.java +++ b/luni/src/main/java/java/util/NavigableSet.java @@ -1,14 +1,13 @@ /* * Written by Doug Lea and Josh Bloch with assistance from members of JCP * JSR-166 Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util; // BEGIN android-note // removed link to collections framework docs -// changed {@link #subSet(Object)} to {@link #subSet} to satisfy DroidDoc // END android-note /** @@ -41,9 +40,9 @@ package java.util; * Comparable} elements intrinsically do not permit {@code null}.) * * <p>Methods - * {@link #subSet subSet(E, E)}, - * {@link #headSet headSet(E)}, and - * {@link #tailSet tailSet(E)} + * {@link #subSet(Object, Object) subSet(E, E)}, + * {@link #headSet(Object) headSet(E)}, and + * {@link #tailSet(Object) tailSet(E)} * are specified to return {@code SortedSet} to allow existing * implementations of {@code SortedSet} to be compatibly retrofitted to * implement {@code NavigableSet}, but extensions and implementations diff --git a/luni/src/main/java/java/util/Observable.java b/luni/src/main/java/java/util/Observable.java index 2c2877e..c984c68 100644 --- a/luni/src/main/java/java/util/Observable.java +++ b/luni/src/main/java/java/util/Observable.java @@ -49,7 +49,7 @@ public class Observable { */ public void addObserver(Observer observer) { if (observer == null) { - throw new NullPointerException(); + throw new NullPointerException("observer == null"); } synchronized (this) { if (!observers.contains(observer)) diff --git a/luni/src/main/java/java/util/PriorityQueue.java b/luni/src/main/java/java/util/PriorityQueue.java index 10c5968..e09eb05 100644 --- a/luni/src/main/java/java/util/PriorityQueue.java +++ b/luni/src/main/java/java/util/PriorityQueue.java @@ -186,7 +186,7 @@ public class PriorityQueue<E> extends AbstractQueue<E> implements Serializable { */ public boolean offer(E o) { if (o == null) { - throw new NullPointerException(); + throw new NullPointerException("o == null"); } growToSize(size + 1); elements[size] = o; @@ -387,7 +387,7 @@ public class PriorityQueue<E> extends AbstractQueue<E> implements Serializable { private void initSize(Collection<? extends E> c) { if (c == null) { - throw new NullPointerException(); + throw new NullPointerException("c == null"); } if (c.isEmpty()) { elements = newElementArray(1); diff --git a/luni/src/main/java/java/util/Properties.java b/luni/src/main/java/java/util/Properties.java index 1731ad8..57c6a00 100644 --- a/luni/src/main/java/java/util/Properties.java +++ b/luni/src/main/java/java/util/Properties.java @@ -243,7 +243,7 @@ public class Properties extends Hashtable<Object, Object> { */ public synchronized void load(InputStream in) throws IOException { if (in == null) { - throw new NullPointerException(); + throw new NullPointerException("in == null"); } load(new InputStreamReader(in, "ISO-8859-1")); } @@ -276,7 +276,7 @@ public class Properties extends Hashtable<Object, Object> { @SuppressWarnings("fallthrough") public synchronized void load(Reader in) throws IOException { if (in == null) { - throw new NullPointerException(); + throw new NullPointerException("in == null"); } int mode = NONE, unicode = 0, count = 0; char nextChar, buf[] = new char[40]; @@ -578,7 +578,7 @@ public class Properties extends Hashtable<Object, Object> { public synchronized void loadFromXML(InputStream in) throws IOException, InvalidPropertiesFormatException { if (in == null) { - throw new NullPointerException(); + throw new NullPointerException("in == null"); } if (builder == null) { @@ -690,8 +690,10 @@ public class Properties extends Hashtable<Object, Object> { public synchronized void storeToXML(OutputStream os, String comment, String encoding) throws IOException { - if (os == null || encoding == null) { - throw new NullPointerException(); + if (os == null) { + throw new NullPointerException("os == null"); + } else if (encoding == null) { + throw new NullPointerException("encoding == null"); } /* diff --git a/luni/src/main/java/java/util/PropertyResourceBundle.java b/luni/src/main/java/java/util/PropertyResourceBundle.java index 4029ee1..dbbd139 100644 --- a/luni/src/main/java/java/util/PropertyResourceBundle.java +++ b/luni/src/main/java/java/util/PropertyResourceBundle.java @@ -46,7 +46,7 @@ public class PropertyResourceBundle extends ResourceBundle { */ public PropertyResourceBundle(InputStream stream) throws IOException { if (stream == null) { - throw new NullPointerException(); + throw new NullPointerException("stream == null"); } resources = new Properties(); resources.load(stream); diff --git a/luni/src/main/java/java/util/Queue.java b/luni/src/main/java/java/util/Queue.java index 5aef944..8b465e6 100644 --- a/luni/src/main/java/java/util/Queue.java +++ b/luni/src/main/java/java/util/Queue.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util; diff --git a/luni/src/main/java/java/util/Random.java b/luni/src/main/java/java/util/Random.java index 7ce74cc..b0a92ff 100644 --- a/luni/src/main/java/java/util/Random.java +++ b/luni/src/main/java/java/util/Random.java @@ -140,25 +140,23 @@ public class Random implements Serializable { * section 3.4.1, subsection C, algorithm P. */ public synchronized double nextGaussian() { - if (haveNextNextGaussian) { // if X1 has been returned, return the - // second Gaussian + if (haveNextNextGaussian) { haveNextNextGaussian = false; return nextNextGaussian; } double v1, v2, s; do { - v1 = 2 * nextDouble() - 1; // Generates two independent random - // variables U1, U2 + v1 = 2 * nextDouble() - 1; v2 = 2 * nextDouble() - 1; s = v1 * v1 + v2 * v2; - } while (s >= 1); - double norm = Math.sqrt(-2 * Math.log(s) / s); - nextNextGaussian = v2 * norm; // should that not be norm instead - // of multiplier ? + } while (s >= 1 || s == 0); + + // The specification says this uses StrictMath. + double multiplier = StrictMath.sqrt(-2 * StrictMath.log(s) / s); + nextNextGaussian = v2 * multiplier; haveNextNextGaussian = true; - return v1 * norm; // should that not be norm instead of multiplier - // ? + return v1 * multiplier; } /** diff --git a/luni/src/main/java/java/util/ResourceBundle.java b/luni/src/main/java/java/util/ResourceBundle.java index ff38b5b..f5c8285 100644 --- a/luni/src/main/java/java/util/ResourceBundle.java +++ b/luni/src/main/java/java/util/ResourceBundle.java @@ -211,8 +211,10 @@ public abstract class ResourceBundle { */ public static ResourceBundle getBundle(String bundleName, Locale locale, ClassLoader loader) throws MissingResourceException { - if (loader == null || bundleName == null) { - throw new NullPointerException(); + if (loader == null) { + throw new NullPointerException("loader == null"); + } else if (bundleName == null) { + throw new NullPointerException("bundleName == null"); } Locale defaultLocale = Locale.getDefault(); if (!cacheLocale.equals(defaultLocale)) { @@ -610,14 +612,14 @@ public abstract class ResourceBundle { public static void clearCache(ClassLoader loader) { if (loader == null) { - throw new NullPointerException(); + throw new NullPointerException("loader == null"); } cache.remove(loader); } public boolean containsKey(String key) { if (key == null) { - throw new NullPointerException(); + throw new NullPointerException("key == null"); } return keySet().contains(key); } @@ -665,8 +667,10 @@ public abstract class ResourceBundle { @Override public Locale getFallbackLocale(String baseName, Locale locale) { - if (baseName == null || locale == null) { - throw new NullPointerException(); + if (baseName == null) { + throw new NullPointerException("baseName == null"); + } else if (locale == null) { + throw new NullPointerException("locale == null"); } return null; } @@ -804,8 +808,10 @@ public abstract class ResourceBundle { * {@code locale}. */ public List<Locale> getCandidateLocales(String baseName, Locale locale) { - if (baseName == null || locale == null) { - throw new NullPointerException(); + if (baseName == null) { + throw new NullPointerException("baseName == null"); + } else if (locale == null) { + throw new NullPointerException("locale == null"); } List<Locale> retList = new ArrayList<Locale>(); String language = locale.getLanguage(); @@ -829,7 +835,7 @@ public abstract class ResourceBundle { */ public List<String> getFormats(String baseName) { if (baseName == null) { - throw new NullPointerException(); + throw new NullPointerException("baseName == null"); } return format; } @@ -838,8 +844,10 @@ public abstract class ResourceBundle { * Returns the fallback locale for {@code baseName} in {@code locale}. */ public Locale getFallbackLocale(String baseName, Locale locale) { - if (baseName == null || locale == null) { - throw new NullPointerException(); + if (baseName == null) { + throw new NullPointerException("baseName == null"); + } else if (locale == null) { + throw new NullPointerException("locale == null"); } if (Locale.getDefault() != locale) { return Locale.getDefault(); @@ -872,8 +880,10 @@ public abstract class ResourceBundle { String format, ClassLoader loader, boolean reload) throws IllegalAccessException, InstantiationException, IOException { - if (format == null || loader == null) { - throw new NullPointerException(); + if (format == null) { + throw new NullPointerException("format == null"); + } else if (loader == null) { + throw new NullPointerException("loader == null"); } final String bundleName = toBundleName(baseName, locale); final ClassLoader clsloader = loader; @@ -938,8 +948,10 @@ public abstract class ResourceBundle { * default is TTL_NO_EXPIRATION_CONTROL. */ public long getTimeToLive(String baseName, Locale locale) { - if (baseName == null || locale == null) { - throw new NullPointerException(); + if (baseName == null) { + throw new NullPointerException("baseName == null"); + } else if (locale == null) { + throw new NullPointerException("locale == null"); } return TTL_NO_EXPIRATION_CONTROL; } @@ -966,7 +978,7 @@ public abstract class ResourceBundle { long loadTime) { if (bundle == null) { // FIXME what's the use of bundle? - throw new NullPointerException(); + throw new NullPointerException("bundle == null"); } String bundleName = toBundleName(baseName, locale); String suffix = format; @@ -1004,7 +1016,7 @@ public abstract class ResourceBundle { final String preString = UNDER_SCORE; final String underline = UNDER_SCORE; if (baseName == null) { - throw new NullPointerException(); + throw new NullPointerException("baseName == null"); } StringBuilder ret = new StringBuilder(); StringBuilder prefix = new StringBuilder(); @@ -1044,7 +1056,7 @@ public abstract class ResourceBundle { */ public final String toResourceName(String bundleName, String suffix) { if (suffix == null) { - throw new NullPointerException(); + throw new NullPointerException("suffix == null"); } StringBuilder ret = new StringBuilder(bundleName.replace('.', '/')); ret.append('.'); diff --git a/luni/src/main/java/java/util/Scanner.java b/luni/src/main/java/java/util/Scanner.java index 8f889b3..5f7d0e3 100644 --- a/luni/src/main/java/java/util/Scanner.java +++ b/luni/src/main/java/java/util/Scanner.java @@ -241,7 +241,7 @@ public final class Scanner implements Iterator<String> { */ public Scanner(Readable src) { if (src == null) { - throw new NullPointerException(); + throw new NullPointerException("src == null"); } input = src; initialization(); @@ -1664,7 +1664,7 @@ public final class Scanner implements Iterator<String> { */ public Scanner useLocale(Locale l) { if (l == null) { - throw new NullPointerException(); + throw new NullPointerException("l == null"); } this.locale = l; return this; @@ -1724,7 +1724,7 @@ public final class Scanner implements Iterator<String> { */ private void checkNull(Pattern pattern) { if (pattern == null) { - throw new NullPointerException(); + throw new NullPointerException("pattern == null"); } } diff --git a/luni/src/main/java/java/util/ServiceLoader.java b/luni/src/main/java/java/util/ServiceLoader.java index beacaab..016ab3f 100644 --- a/luni/src/main/java/java/util/ServiceLoader.java +++ b/luni/src/main/java/java/util/ServiceLoader.java @@ -78,7 +78,7 @@ public final class ServiceLoader<S> implements Iterable<S> { // It makes no sense for service to be null. // classLoader is null if you want the system class loader. if (service == null) { - throw new NullPointerException(); + throw new NullPointerException("service == null"); } this.service = service; this.classLoader = classLoader; diff --git a/luni/src/main/java/java/util/SimpleTimeZone.java b/luni/src/main/java/java/util/SimpleTimeZone.java index 6c9b443..93dc88e 100644 --- a/luni/src/main/java/java/util/SimpleTimeZone.java +++ b/luni/src/main/java/java/util/SimpleTimeZone.java @@ -217,10 +217,17 @@ public class SimpleTimeZone extends TimeZone { throw new IllegalArgumentException("Invalid daylightSavings: " + daylightSavings); } dstSavings = daylightSavings; - // TODO: do we need to set useDaylight is dstSavings != 0? - setStartRule(startMonth, startDay, startDayOfWeek, startTime); - setEndRule(endMonth, endDay, endDayOfWeek, endTime); + this.startMonth = startMonth; + this.startDay = startDay; + this.startDayOfWeek = startDayOfWeek; + this.startTime = startTime; + setStartMode(); + this.endMonth = endMonth; + this.endDay = endDay; + this.endDayOfWeek = endDayOfWeek; + this.endTime = endTime; + setEndMode(); } /** diff --git a/luni/src/main/java/java/util/StringTokenizer.java b/luni/src/main/java/java/util/StringTokenizer.java index 1b07604..e6686e8 100644 --- a/luni/src/main/java/java/util/StringTokenizer.java +++ b/luni/src/main/java/java/util/StringTokenizer.java @@ -91,13 +91,13 @@ public class StringTokenizer implements Enumeration<Object> { */ public StringTokenizer(String string, String delimiters, boolean returnDelimiters) { - if (string != null) { - this.string = string; - this.delimiters = delimiters; - this.returnDelimiters = returnDelimiters; - this.position = 0; - } else - throw new NullPointerException(); + if (string == null) { + throw new NullPointerException("string == null"); + } + this.string = string; + this.delimiters = delimiters; + this.returnDelimiters = returnDelimiters; + this.position = 0; } /** @@ -143,7 +143,7 @@ public class StringTokenizer implements Enumeration<Object> { */ public boolean hasMoreTokens() { if (delimiters == null) { - throw new NullPointerException(); + throw new NullPointerException("delimiters == null"); } int length = string.length(); if (position < length) { @@ -180,7 +180,7 @@ public class StringTokenizer implements Enumeration<Object> { */ public String nextToken() { if (delimiters == null) { - throw new NullPointerException(); + throw new NullPointerException("delimiters == null"); } int i = position; int length = string.length(); diff --git a/luni/src/main/java/java/util/TimeZone.java b/luni/src/main/java/java/util/TimeZone.java index 34763cc..85011bc 100644 --- a/luni/src/main/java/java/util/TimeZone.java +++ b/luni/src/main/java/java/util/TimeZone.java @@ -17,7 +17,10 @@ package java.util; +import java.io.IOException; import java.io.Serializable; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import libcore.icu.TimeZones; import libcore.util.ZoneInfoDB; @@ -28,7 +31,7 @@ import libcore.util.ZoneInfoDB; * <p>Most applications will use {@link #getDefault} which returns a {@code TimeZone} based on * the time zone where the program is running. * - * <p>You can also get a specific {@code TimeZone} {@link #getTimeZone by id}. + * <p>You can also get a specific {@code TimeZone} {@link #getTimeZone by Olson ID}. * * <p>It is highly unlikely you'll ever want to use anything but the factory methods yourself. * Let classes like {@link Calendar} and {@link java.text.SimpleDateFormat} do the date @@ -64,6 +67,8 @@ import libcore.util.ZoneInfoDB; public abstract class TimeZone implements Serializable, Cloneable { private static final long serialVersionUID = 3581463369166924961L; + private static final Pattern CUSTOM_ZONE_ID_PATTERN = Pattern.compile("^GMT[-+](\\d{1,2})(:?(\\d\\d))?$"); + /** * The short display name style, such as {@code PDT}. Requests for this * style may yield GMT offsets like {@code GMT-08:00}. @@ -76,7 +81,8 @@ public abstract class TimeZone implements Serializable, Cloneable { */ public static final int LONG = 1; - static final TimeZone GMT = new SimpleTimeZone(0, "GMT"); // Greenwich Mean Time + private static final TimeZone GMT = new SimpleTimeZone(0, "GMT"); + private static final TimeZone UTC = new SimpleTimeZone(0, "UTC"); private static TimeZone defaultTimeZone; @@ -214,16 +220,27 @@ public abstract class TimeZone implements Serializable, Cloneable { } /** - * Returns the daylight savings offset in milliseconds for this time zone. - * The base implementation returns {@code 3600000} (1 hour) for time zones - * that use daylight savings time and {@code 0} for timezones that do not. - * Subclasses should override this method for other daylight savings - * offsets. + * Returns the latest daylight savings in milliseconds for this time zone, relative + * to this time zone's regular UTC offset (as returned by {@link #getRawOffset}). + * + * <p>This class returns {@code 3600000} (1 hour) for time zones + * that use daylight savings time and {@code 0} for timezones that do not, + * leaving it to subclasses to override this method for other daylight savings + * offsets. (There are time zones, such as {@code Australia/Lord_Howe}, + * that use other values.) * - * <p>Note that this method doesn't tell you whether or not to apply the + * <p>Note that this method doesn't tell you whether or not to <i>apply</i> the * offset: you need to call {@code inDaylightTime} for the specific time * you're interested in. If this method returns a non-zero offset, that only * tells you that this {@code TimeZone} sometimes observes daylight savings. + * + * <p>Note also that this method doesn't necessarily return the value you need + * to apply to the time you're working with. This value can and does change over + * time for a given time zone. + * + * <p>It's highly unlikely that you should ever call this method. You + * probably want {@link #getOffset} instead, which tells you the offset + * for a specific point in time, and takes daylight savings into account for you. */ public int getDSTSavings() { return useDaylightTime() ? 3600000 : 0; @@ -265,17 +282,19 @@ public abstract class TimeZone implements Serializable, Cloneable { public abstract int getRawOffset(); /** - * Returns a {@code TimeZone} suitable for {@code id}, or {@code GMT} for unknown ids. + * Returns a {@code TimeZone} corresponding to the given {@code id}, or {@code GMT} + * for unknown ids. * - * <p>An id can be an Olson name of the form <i>Area</i>/<i>Location</i>, such + * <p>An ID can be an Olson name of the form <i>Area</i>/<i>Location</i>, such * as {@code America/Los_Angeles}. The {@link #getAvailableIDs} method returns * the supported names. * - * <p>This method can also create a custom {@code TimeZone} using the following - * syntax: {@code GMT[+|-]hh[[:]mm]}. For example, {@code TimeZone.getTimeZone("GMT+14:00")} - * would return an object with a raw offset of +14 hours from UTC, and which does <i>not</i> - * use daylight savings. These are rarely useful, because they don't correspond to time - * zones actually in use. + * <p>This method can also create a custom {@code TimeZone} given an ID with the following + * syntax: {@code GMT[+|-]hh[[:]mm]}. For example, {@code "GMT+05:00"}, {@code "GMT+0500"}, + * {@code "GMT+5:00"}, {@code "GMT+500"}, {@code "GMT+05"}, and {@code "GMT+5"} all return + * an object with a raw offset of +5 hours from UTC, and which does <i>not</i> use daylight + * savings. These are rarely useful, because they don't correspond to time zones actually + * in use by humans. * * <p>Other than the special cases "UTC" and "GMT" (which are synonymous in this context, * both corresponding to UTC), Android does not support the deprecated three-letter time @@ -285,80 +304,66 @@ public abstract class TimeZone implements Serializable, Cloneable { if (id == null) { throw new NullPointerException("id == null"); } - TimeZone zone = ZoneInfoDB.getTimeZone(id); - if (zone != null) { - return zone; + + // Special cases? These can clone an existing instance. + // TODO: should we just add a cache to ZoneInfoDB instead? + if (id.length() == 3) { + if (id.equals("GMT")) { + return (TimeZone) GMT.clone(); + } + if (id.equals("UTC")) { + return (TimeZone) UTC.clone(); + } } + + // In the database? + TimeZone zone = null; + try { + zone = ZoneInfoDB.makeTimeZone(id); + } catch (IOException ignored) { + } + + // Custom time zone? if (zone == null && id.length() > 3 && id.startsWith("GMT")) { zone = getCustomTimeZone(id); } - if (zone == null) { - zone = (TimeZone) GMT.clone(); - } - return zone; + + // We never return null; on failure we return the equivalent of "GMT". + return (zone != null) ? zone : (TimeZone) GMT.clone(); } /** - * Returns a new SimpleTimeZone for an id of the form "GMT[+|-]hh[[:]mm]", or null. + * Returns a new SimpleTimeZone for an ID of the form "GMT[+|-]hh[[:]mm]", or null. */ private static TimeZone getCustomTimeZone(String id) { - char sign = id.charAt(3); - if (sign != '+' && sign != '-') { - return null; - } - int[] position = new int[1]; - String formattedName = formatTimeZoneName(id, 4); - int hour = parseNumber(formattedName, 4, position); - if (hour < 0 || hour > 23) { + Matcher m = CUSTOM_ZONE_ID_PATTERN.matcher(id); + if (!m.matches()) { return null; } - int index = position[0]; - if (index == -1) { - return null; - } - int raw = hour * 3600000; - if (index < formattedName.length() && formattedName.charAt(index) == ':') { - int minute = parseNumber(formattedName, index + 1, position); - if (position[0] == -1 || minute < 0 || minute > 59) { - return null; - } - raw += minute * 60000; - } else if (hour >= 30 || index > 6) { - raw = (hour / 100 * 3600000) + (hour % 100 * 60000); - } - if (sign == '-') { - raw = -raw; - } - return new SimpleTimeZone(raw, formattedName); - } - private static String formatTimeZoneName(String name, int offset) { - StringBuilder buf = new StringBuilder(); - int index = offset, length = name.length(); - buf.append(name.substring(0, offset)); - - while (index < length) { - if (Character.digit(name.charAt(index), 10) != -1) { - buf.append(name.charAt(index)); - if ((length - (index + 1)) == 2) { - buf.append(':'); - } - } else if (name.charAt(index) == ':') { - buf.append(':'); + int hour; + int minute = 0; + try { + hour = Integer.parseInt(m.group(1)); + if (m.group(3) != null) { + minute = Integer.parseInt(m.group(3)); } - index++; + } catch (NumberFormatException impossible) { + throw new AssertionError(impossible); } - if (buf.toString().indexOf(":") == -1) { - buf.append(':'); - buf.append("00"); + if (hour < 0 || hour > 23 || minute < 0 || minute > 59) { + return null; } - if (buf.toString().indexOf(":") == 5) { - buf.insert(4, '0'); + char sign = id.charAt(3); + int raw = (hour * 3600000) + (minute * 60000); + if (sign == '-') { + raw = -raw; } - return buf.toString(); + String cleanId = String.format("GMT%c%02d:%02d", sign, hour, minute); + return new SimpleTimeZone(raw, cleanId); } /** @@ -380,17 +385,6 @@ public abstract class TimeZone implements Serializable, Cloneable { */ public abstract boolean inDaylightTime(Date time); - private static int parseNumber(String string, int offset, int[] position) { - int index = offset, length = string.length(), digit, result = 0; - while (index < length - && (digit = Character.digit(string.charAt(index), 10)) != -1) { - index++; - result = result * 10 + digit; - } - position[0] = index == offset ? -1 : index; - return result; - } - /** * Overrides the default time zone for the current process only. * @@ -411,7 +405,7 @@ public abstract class TimeZone implements Serializable, Cloneable { */ public void setID(String id) { if (id == null) { - throw new NullPointerException(); + throw new NullPointerException("id == null"); } ID = id; } diff --git a/luni/src/main/java/java/util/Timer.java b/luni/src/main/java/java/util/Timer.java index b4f7a83..afc745c 100644 --- a/luni/src/main/java/java/util/Timer.java +++ b/luni/src/main/java/java/util/Timer.java @@ -362,7 +362,7 @@ public class Timer { */ public Timer(String name, boolean isDaemon) { if (name == null) { - throw new NullPointerException("name is null"); + throw new NullPointerException("name == null"); } this.impl = new TimerImpl(name, isDaemon); this.finalizer = new FinalizerHelper(impl); diff --git a/luni/src/main/java/java/util/UUID.java b/luni/src/main/java/java/util/UUID.java index a932bb2..8ac0a63 100644 --- a/luni/src/main/java/java/util/UUID.java +++ b/luni/src/main/java/java/util/UUID.java @@ -142,7 +142,7 @@ public final class UUID implements Serializable, Comparable<UUID> { */ public static UUID nameUUIDFromBytes(byte[] name) { if (name == null) { - throw new NullPointerException(); + throw new NullPointerException("name == null"); } try { MessageDigest md = MessageDigest.getInstance("MD5"); @@ -179,7 +179,7 @@ public final class UUID implements Serializable, Comparable<UUID> { */ public static UUID fromString(String uuid) { if (uuid == null) { - throw new NullPointerException(); + throw new NullPointerException("uuid == null"); } int[] position = new int[5]; diff --git a/luni/src/main/java/java/util/UnknownFormatConversionException.java b/luni/src/main/java/java/util/UnknownFormatConversionException.java index e26de91..29af4e1 100644 --- a/luni/src/main/java/java/util/UnknownFormatConversionException.java +++ b/luni/src/main/java/java/util/UnknownFormatConversionException.java @@ -35,7 +35,7 @@ public class UnknownFormatConversionException extends IllegalFormatException { */ public UnknownFormatConversionException(String s) { if (s == null) { - throw new NullPointerException(); + throw new NullPointerException("s == null"); } this.s = s; } diff --git a/luni/src/main/java/java/util/UnknownFormatFlagsException.java b/luni/src/main/java/java/util/UnknownFormatFlagsException.java index 9daa3f1..990432b 100644 --- a/luni/src/main/java/java/util/UnknownFormatFlagsException.java +++ b/luni/src/main/java/java/util/UnknownFormatFlagsException.java @@ -37,7 +37,7 @@ public class UnknownFormatFlagsException extends IllegalFormatException { */ public UnknownFormatFlagsException(String f) { if (f == null) { - throw new NullPointerException(); + throw new NullPointerException("f == null"); } flags = f; } diff --git a/luni/src/main/java/java/util/concurrent/AbstractExecutorService.java b/luni/src/main/java/java/util/concurrent/AbstractExecutorService.java index 36fcecc..a7f7745 100644 --- a/luni/src/main/java/java/util/concurrent/AbstractExecutorService.java +++ b/luni/src/main/java/java/util/concurrent/AbstractExecutorService.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; diff --git a/luni/src/main/java/java/util/concurrent/ArrayBlockingQueue.java b/luni/src/main/java/java/util/concurrent/ArrayBlockingQueue.java index a622832..e30ab67 100644 --- a/luni/src/main/java/java/util/concurrent/ArrayBlockingQueue.java +++ b/luni/src/main/java/java/util/concurrent/ArrayBlockingQueue.java @@ -1,12 +1,17 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; -import java.util.concurrent.locks.*; -import java.util.*; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; +import java.util.AbstractQueue; +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.lang.ref.WeakReference; // BEGIN android-note // removed link to collections framework docs @@ -73,11 +78,20 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> /** Main lock guarding all access */ final ReentrantLock lock; + /** Condition for waiting takes */ private final Condition notEmpty; + /** Condition for waiting puts */ private final Condition notFull; + /** + * Shared state for currently active iterators, or null if there + * are known not to be any. Allows queue operations to update + * iterator state. + */ + transient Itrs itrs = null; + // Internal helper methods /** @@ -94,16 +108,12 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> return ((i == 0) ? items.length : i) - 1; } - @SuppressWarnings("unchecked") - static <E> E cast(Object item) { - return (E) item; - } - /** * Returns item at index i. */ final E itemAt(int i) { - return this.<E>cast(items[i]); + @SuppressWarnings("unchecked") E x = (E) items[i]; + return x; } /** @@ -120,10 +130,12 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> * Inserts element at current put position, advances, and signals. * Call only when holding lock. */ - private void insert(E x) { + private void enqueue(E x) { + // assert lock.getHoldCount() == 1; + // assert items[putIndex] == null; items[putIndex] = x; putIndex = inc(putIndex); - ++count; + count++; notEmpty.signal(); } @@ -131,42 +143,57 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> * Extracts element at current take position, advances, and signals. * Call only when holding lock. */ - private E extract() { + private E dequeue() { + // assert lock.getHoldCount() == 1; + // assert items[takeIndex] != null; final Object[] items = this.items; - E x = this.<E>cast(items[takeIndex]); + @SuppressWarnings("unchecked") E x = (E) items[takeIndex]; items[takeIndex] = null; takeIndex = inc(takeIndex); - --count; + count--; + if (itrs != null) + itrs.elementDequeued(); notFull.signal(); return x; } /** - * Deletes item at position i. - * Utility for remove and iterator.remove. + * Deletes item at array index removeIndex. + * Utility for remove(Object) and iterator.remove. * Call only when holding lock. */ - void removeAt(int i) { + void removeAt(final int removeIndex) { + // assert lock.getHoldCount() == 1; + // assert items[removeIndex] != null; + // assert removeIndex >= 0 && removeIndex < items.length; final Object[] items = this.items; - // if removing front item, just advance - if (i == takeIndex) { + if (removeIndex == takeIndex) { + // removing front item; just advance items[takeIndex] = null; takeIndex = inc(takeIndex); + count--; + if (itrs != null) + itrs.elementDequeued(); } else { + // an "interior" remove + // slide over all others up through putIndex. - for (;;) { - int nexti = inc(i); - if (nexti != putIndex) { - items[i] = items[nexti]; - i = nexti; + final int putIndex = this.putIndex; + for (int i = removeIndex;;) { + int next = inc(i); + if (next != putIndex) { + items[i] = items[next]; + i = next; } else { items[i] = null; - putIndex = i; + this.putIndex = i; break; } } + count--; + if (itrs != null) + itrs.removedAt(removeIndex); } - --count; notFull.signal(); } @@ -271,7 +298,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> if (count == items.length) return false; else { - insert(e); + enqueue(e); return true; } } finally { @@ -293,7 +320,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> try { while (count == items.length) notFull.await(); - insert(e); + enqueue(e); } finally { lock.unlock(); } @@ -320,7 +347,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> return false; nanos = notFull.awaitNanos(nanos); } - insert(e); + enqueue(e); return true; } finally { lock.unlock(); @@ -331,7 +358,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> final ReentrantLock lock = this.lock; lock.lock(); try { - return (count == 0) ? null : extract(); + return (count == 0) ? null : dequeue(); } finally { lock.unlock(); } @@ -343,7 +370,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> try { while (count == 0) notEmpty.await(); - return extract(); + return dequeue(); } finally { lock.unlock(); } @@ -359,7 +386,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> return null; nanos = notEmpty.awaitNanos(nanos); } - return extract(); + return dequeue(); } finally { lock.unlock(); } @@ -438,11 +465,15 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> final ReentrantLock lock = this.lock; lock.lock(); try { - for (int i = takeIndex, k = count; k > 0; i = inc(i), k--) { - if (o.equals(items[i])) { - removeAt(i); - return true; - } + if (count > 0) { + final int putIndex = this.putIndex; + int i = takeIndex; + do { + if (o.equals(items[i])) { + removeAt(i); + return true; + } + } while ((i = inc(i)) != putIndex); } return false; } finally { @@ -464,9 +495,14 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> final ReentrantLock lock = this.lock; lock.lock(); try { - for (int i = takeIndex, k = count; k > 0; i = inc(i), k--) - if (o.equals(items[i])) - return true; + if (count > 0) { + final int putIndex = this.putIndex; + int i = takeIndex; + do { + if (o.equals(items[i])) + return true; + } while ((i = inc(i)) != putIndex); + } return false; } finally { lock.unlock(); @@ -522,8 +558,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> * The following code can be used to dump the queue into a newly * allocated array of {@code String}: * - * <pre> - * String[] y = x.toArray(new String[0]);</pre> + * <pre> {@code String[] y = x.toArray(new String[0]);}</pre> * * Note that {@code toArray(new Object[0])} is identical in function to * {@code toArray()}. @@ -589,12 +624,20 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> final ReentrantLock lock = this.lock; lock.lock(); try { - for (int i = takeIndex, k = count; k > 0; i = inc(i), k--) - items[i] = null; - count = 0; - putIndex = 0; - takeIndex = 0; - notFull.signalAll(); + int k = count; + if (k > 0) { + final int putIndex = this.putIndex; + int i = takeIndex; + do { + items[i] = null; + } while ((i = inc(i)) != putIndex); + takeIndex = putIndex; + count = 0; + if (itrs != null) + itrs.queueIsEmpty(); + for (; k > 0 && lock.hasWaiters(notFull); k--) + notFull.signal(); + } } finally { lock.unlock(); } @@ -607,32 +650,7 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> * @throws IllegalArgumentException {@inheritDoc} */ public int drainTo(Collection<? super E> c) { - checkNotNull(c); - if (c == this) - throw new IllegalArgumentException(); - final Object[] items = this.items; - final ReentrantLock lock = this.lock; - lock.lock(); - try { - int i = takeIndex; - int n = 0; - int max = count; - while (n < max) { - c.add(this.<E>cast(items[i])); - items[i] = null; - i = inc(i); - ++n; - } - if (n > 0) { - count = 0; - putIndex = 0; - takeIndex = 0; - notFull.signalAll(); - } - return n; - } finally { - lock.unlock(); - } + return drainTo(c, Integer.MAX_VALUE); } /** @@ -651,21 +669,33 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> final ReentrantLock lock = this.lock; lock.lock(); try { - int i = takeIndex; - int n = 0; - int max = (maxElements < count) ? maxElements : count; - while (n < max) { - c.add(this.<E>cast(items[i])); - items[i] = null; - i = inc(i); - ++n; - } - if (n > 0) { - count -= n; - takeIndex = i; - notFull.signalAll(); + int n = Math.min(maxElements, count); + int take = takeIndex; + int i = 0; + try { + while (i < n) { + @SuppressWarnings("unchecked") E x = (E) items[take]; + c.add(x); + items[take] = null; + take = inc(take); + i++; + } + return n; + } finally { + // Restore invariants even if c.add() threw + if (i > 0) { + count -= i; + takeIndex = take; + if (itrs != null) { + if (count == 0) + itrs.queueIsEmpty(); + else if (i > take) + itrs.takeIndexWrapped(); + } + for (; i > 0 && lock.hasWaiters(notFull); i--) + notFull.signal(); + } } - return n; } finally { lock.unlock(); } @@ -675,12 +705,12 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> * Returns an iterator over the elements in this queue in proper sequence. * The elements will be returned in order from first (head) to last (tail). * - * <p>The returned {@code Iterator} is a "weakly consistent" iterator that + * <p>The returned iterator is a "weakly consistent" iterator that * will never throw {@link java.util.ConcurrentModificationException - * ConcurrentModificationException}, - * and guarantees to traverse elements as they existed upon - * construction of the iterator, and may (but is not guaranteed to) - * reflect any modifications subsequent to construction. + * ConcurrentModificationException}, and guarantees to traverse + * elements as they existed upon construction of the iterator, and + * may (but is not guaranteed to) reflect any modifications + * subsequent to construction. * * @return an iterator over the elements in this queue in proper sequence */ @@ -689,88 +719,627 @@ public class ArrayBlockingQueue<E> extends AbstractQueue<E> } /** - * Iterator for ArrayBlockingQueue. To maintain weak consistency - * with respect to puts and takes, we (1) read ahead one slot, so - * as to not report hasNext true but then not have an element to - * return -- however we later recheck this slot to use the most - * current value; (2) ensure that each array slot is traversed at - * most once (by tracking "remaining" elements); (3) skip over - * null slots, which can occur if takes race ahead of iterators. - * However, for circular array-based queues, we cannot rely on any - * well established definition of what it means to be weakly - * consistent with respect to interior removes since these may - * require slot overwrites in the process of sliding elements to - * cover gaps. So we settle for resiliency, operating on - * established apparent nexts, which may miss some elements that - * have moved between calls to next. + * Shared data between iterators and their queue, allowing queue + * modifications to update iterators when elements are removed. + * + * This adds a lot of complexity for the sake of correctly + * handling some uncommon operations, but the combination of + * circular-arrays and supporting interior removes (i.e., those + * not at head) would cause iterators to sometimes lose their + * places and/or (re)report elements they shouldn't. To avoid + * this, when a queue has one or more iterators, it keeps iterator + * state consistent by: + * + * (1) keeping track of the number of "cycles", that is, the + * number of times takeIndex has wrapped around to 0. + * (2) notifying all iterators via the callback removedAt whenever + * an interior element is removed (and thus other elements may + * be shifted). + * + * These suffice to eliminate iterator inconsistencies, but + * unfortunately add the secondary responsibility of maintaining + * the list of iterators. We track all active iterators in a + * simple linked list (accessed only when the queue's lock is + * held) of weak references to Itr. The list is cleaned up using + * 3 different mechanisms: + * + * (1) Whenever a new iterator is created, do some O(1) checking for + * stale list elements. + * + * (2) Whenever takeIndex wraps around to 0, check for iterators + * that have been unused for more than one wrap-around cycle. + * + * (3) Whenever the queue becomes empty, all iterators are notified + * and this entire data structure is discarded. + * + * So in addition to the removedAt callback that is necessary for + * correctness, iterators have the shutdown and takeIndexWrapped + * callbacks that help remove stale iterators from the list. + * + * Whenever a list element is examined, it is expunged if either + * the GC has determined that the iterator is discarded, or if the + * iterator reports that it is "detached" (does not need any + * further state updates). Overhead is maximal when takeIndex + * never advances, iterators are discarded before they are + * exhausted, and all removals are interior removes, in which case + * all stale iterators are discovered by the GC. But even in this + * case we don't increase the amortized complexity. + * + * Care must be taken to keep list sweeping methods from + * reentrantly invoking another such method, causing subtle + * corruption bugs. + */ + class Itrs { + + /** + * Node in a linked list of weak iterator references. + */ + private class Node extends WeakReference<Itr> { + Node next; + + Node(Itr iterator, Node next) { + super(iterator); + this.next = next; + } + } + + /** Incremented whenever takeIndex wraps around to 0 */ + int cycles = 0; + + /** Linked list of weak iterator references */ + private Node head; + + /** Used to expunge stale iterators */ + private Node sweeper = null; + + private static final int SHORT_SWEEP_PROBES = 4; + private static final int LONG_SWEEP_PROBES = 16; + + Itrs(Itr initial) { + register(initial); + } + + /** + * Sweeps itrs, looking for and expunging stale iterators. + * If at least one was found, tries harder to find more. + * Called only from iterating thread. + * + * @param tryHarder whether to start in try-harder mode, because + * there is known to be at least one iterator to collect + */ + void doSomeSweeping(boolean tryHarder) { + // assert lock.getHoldCount() == 1; + // assert head != null; + int probes = tryHarder ? LONG_SWEEP_PROBES : SHORT_SWEEP_PROBES; + Node o, p; + final Node sweeper = this.sweeper; + boolean passedGo; // to limit search to one full sweep + + if (sweeper == null) { + o = null; + p = head; + passedGo = true; + } else { + o = sweeper; + p = o.next; + passedGo = false; + } + + for (; probes > 0; probes--) { + if (p == null) { + if (passedGo) + break; + o = null; + p = head; + passedGo = true; + } + final Itr it = p.get(); + final Node next = p.next; + if (it == null || it.isDetached()) { + // found a discarded/exhausted iterator + probes = LONG_SWEEP_PROBES; // "try harder" + // unlink p + p.clear(); + p.next = null; + if (o == null) { + head = next; + if (next == null) { + // We've run out of iterators to track; retire + itrs = null; + return; + } + } + else + o.next = next; + } else { + o = p; + } + p = next; + } + + this.sweeper = (p == null) ? null : o; + } + + /** + * Adds a new iterator to the linked list of tracked iterators. + */ + void register(Itr itr) { + // assert lock.getHoldCount() == 1; + head = new Node(itr, head); + } + + /** + * Called whenever takeIndex wraps around to 0. + * + * Notifies all iterators, and expunges any that are now stale. + */ + void takeIndexWrapped() { + // assert lock.getHoldCount() == 1; + cycles++; + for (Node o = null, p = head; p != null;) { + final Itr it = p.get(); + final Node next = p.next; + if (it == null || it.takeIndexWrapped()) { + // unlink p + // assert it == null || it.isDetached(); + p.clear(); + p.next = null; + if (o == null) + head = next; + else + o.next = next; + } else { + o = p; + } + p = next; + } + if (head == null) // no more iterators to track + itrs = null; + } + + /** + * Called whenever an interior remove (not at takeIndex) occured. + * + * Notifies all iterators, and expunges any that are now stale. + */ + void removedAt(int removedIndex) { + for (Node o = null, p = head; p != null;) { + final Itr it = p.get(); + final Node next = p.next; + if (it == null || it.removedAt(removedIndex)) { + // unlink p + // assert it == null || it.isDetached(); + p.clear(); + p.next = null; + if (o == null) + head = next; + else + o.next = next; + } else { + o = p; + } + p = next; + } + if (head == null) // no more iterators to track + itrs = null; + } + + /** + * Called whenever the queue becomes empty. + * + * Notifies all active iterators that the queue is empty, + * clears all weak refs, and unlinks the itrs datastructure. + */ + void queueIsEmpty() { + // assert lock.getHoldCount() == 1; + for (Node p = head; p != null; p = p.next) { + Itr it = p.get(); + if (it != null) { + p.clear(); + it.shutdown(); + } + } + head = null; + itrs = null; + } + + /** + * Called whenever an element has been dequeued (at takeIndex). + */ + void elementDequeued() { + // assert lock.getHoldCount() == 1; + if (count == 0) + queueIsEmpty(); + else if (takeIndex == 0) + takeIndexWrapped(); + } + } + + /** + * Iterator for ArrayBlockingQueue. + * + * To maintain weak consistency with respect to puts and takes, we + * read ahead one slot, so as to not report hasNext true but then + * not have an element to return. + * + * We switch into "detached" mode (allowing prompt unlinking from + * itrs without help from the GC) when all indices are negative, or + * when hasNext returns false for the first time. This allows the + * iterator to track concurrent updates completely accurately, + * except for the corner case of the user calling Iterator.remove() + * after hasNext() returned false. Even in this case, we ensure + * that we don't remove the wrong element by keeping track of the + * expected element to remove, in lastItem. Yes, we may fail to + * remove lastItem from the queue if it moved due to an interleaved + * interior remove while in detached mode. */ private class Itr implements Iterator<E> { - private int remaining; // Number of elements yet to be returned - private int nextIndex; // Index of element to be returned by next - private E nextItem; // Element to be returned by next call to next - private E lastItem; // Element returned by last call to next - private int lastRet; // Index of last element returned, or -1 if none + /** Index to look for new nextItem; NONE at end */ + private int cursor; + + /** Element to be returned by next call to next(); null if none */ + private E nextItem; + + /** Index of nextItem; NONE if none, REMOVED if removed elsewhere */ + private int nextIndex; + + /** Last element returned; null if none or not detached. */ + private E lastItem; + + /** Index of lastItem, NONE if none, REMOVED if removed elsewhere */ + private int lastRet; + + /** Previous value of takeIndex, or DETACHED when detached */ + private int prevTakeIndex; + + /** Previous value of iters.cycles */ + private int prevCycles; + + /** Special index value indicating "not available" or "undefined" */ + private static final int NONE = -1; + + /** + * Special index value indicating "removed elsewhere", that is, + * removed by some operation other than a call to this.remove(). + */ + private static final int REMOVED = -2; + + /** Special value for prevTakeIndex indicating "detached mode" */ + private static final int DETACHED = -3; Itr() { + // assert lock.getHoldCount() == 0; + lastRet = NONE; final ReentrantLock lock = ArrayBlockingQueue.this.lock; lock.lock(); try { - lastRet = -1; - if ((remaining = count) > 0) + if (count == 0) { + // assert itrs == null; + cursor = NONE; + nextIndex = NONE; + prevTakeIndex = DETACHED; + } else { + final int takeIndex = ArrayBlockingQueue.this.takeIndex; + prevTakeIndex = takeIndex; nextItem = itemAt(nextIndex = takeIndex); + cursor = incCursor(takeIndex); + if (itrs == null) { + itrs = new Itrs(this); + } else { + itrs.register(this); // in this order + itrs.doSomeSweeping(false); + } + prevCycles = itrs.cycles; + // assert takeIndex >= 0; + // assert prevTakeIndex == takeIndex; + // assert nextIndex >= 0; + // assert nextItem != null; + } } finally { lock.unlock(); } } + boolean isDetached() { + // assert lock.getHoldCount() == 1; + return prevTakeIndex < 0; + } + + private int incCursor(int index) { + // assert lock.getHoldCount() == 1; + index = inc(index); + if (index == putIndex) + index = NONE; + return index; + } + + /** + * Returns true if index is invalidated by the given number of + * dequeues, starting from prevTakeIndex. + */ + private boolean invalidated(int index, int prevTakeIndex, + long dequeues, int length) { + if (index < 0) + return false; + int distance = index - prevTakeIndex; + if (distance < 0) + distance += length; + return dequeues > distance; + } + + /** + * Adjusts indices to incorporate all dequeues since the last + * operation on this iterator. Call only from iterating thread. + */ + private void incorporateDequeues() { + // assert lock.getHoldCount() == 1; + // assert itrs != null; + // assert !isDetached(); + // assert count > 0; + + final int cycles = itrs.cycles; + final int takeIndex = ArrayBlockingQueue.this.takeIndex; + final int prevCycles = this.prevCycles; + final int prevTakeIndex = this.prevTakeIndex; + + if (cycles != prevCycles || takeIndex != prevTakeIndex) { + final int len = items.length; + // how far takeIndex has advanced since the previous + // operation of this iterator + long dequeues = (cycles - prevCycles) * len + + (takeIndex - prevTakeIndex); + + // Check indices for invalidation + if (invalidated(lastRet, prevTakeIndex, dequeues, len)) + lastRet = REMOVED; + if (invalidated(nextIndex, prevTakeIndex, dequeues, len)) + nextIndex = REMOVED; + if (invalidated(cursor, prevTakeIndex, dequeues, len)) + cursor = takeIndex; + + if (cursor < 0 && nextIndex < 0 && lastRet < 0) + detach(); + else { + this.prevCycles = cycles; + this.prevTakeIndex = takeIndex; + } + } + } + + /** + * Called when itrs should stop tracking this iterator, either + * because there are no more indices to update (cursor < 0 && + * nextIndex < 0 && lastRet < 0) or as a special exception, when + * lastRet >= 0, because hasNext() is about to return false for the + * first time. Call only from iterating thread. + */ + private void detach() { + // Switch to detached mode + // assert lock.getHoldCount() == 1; + // assert cursor == NONE; + // assert nextIndex < 0; + // assert lastRet < 0 || nextItem == null; + // assert lastRet < 0 ^ lastItem != null; + if (prevTakeIndex >= 0) { + // assert itrs != null; + prevTakeIndex = DETACHED; + // try to unlink from itrs (but not too hard) + itrs.doSomeSweeping(true); + } + } + + /** + * For performance reasons, we would like not to acquire a lock in + * hasNext in the common case. To allow for this, we only access + * fields (i.e. nextItem) that are not modified by update operations + * triggered by queue modifications. + */ public boolean hasNext() { - return remaining > 0; + // assert lock.getHoldCount() == 0; + if (nextItem != null) + return true; + noNext(); + return false; + } + + private void noNext() { + final ReentrantLock lock = ArrayBlockingQueue.this.lock; + lock.lock(); + try { + // assert cursor == NONE; + // assert nextIndex == NONE; + if (!isDetached()) { + // assert lastRet >= 0; + incorporateDequeues(); // might update lastRet + if (lastRet >= 0) { + lastItem = itemAt(lastRet); + // assert lastItem != null; + detach(); + } + } + // assert isDetached(); + // assert lastRet < 0 ^ lastItem != null; + } finally { + lock.unlock(); + } } public E next() { + // assert lock.getHoldCount() == 0; + final E x = nextItem; + if (x == null) + throw new NoSuchElementException(); final ReentrantLock lock = ArrayBlockingQueue.this.lock; lock.lock(); try { - if (remaining <= 0) - throw new NoSuchElementException(); + if (!isDetached()) + incorporateDequeues(); + // assert nextIndex != NONE; + // assert lastItem == null; lastRet = nextIndex; - E x = itemAt(nextIndex); // check for fresher value - if (x == null) { - x = nextItem; // we are forced to report old value - lastItem = null; // but ensure remove fails + final int cursor = this.cursor; + if (cursor >= 0) { + nextItem = itemAt(nextIndex = cursor); + // assert nextItem != null; + this.cursor = incCursor(cursor); + } else { + nextIndex = NONE; + nextItem = null; } - else - lastItem = x; - while (--remaining > 0 && // skip over nulls - (nextItem = itemAt(nextIndex = inc(nextIndex))) == null) - ; - return x; } finally { lock.unlock(); } + return x; } public void remove() { + // assert lock.getHoldCount() == 0; final ReentrantLock lock = ArrayBlockingQueue.this.lock; lock.lock(); try { - int i = lastRet; - if (i == -1) + if (!isDetached()) + incorporateDequeues(); // might update lastRet or detach + final int lastRet = this.lastRet; + this.lastRet = NONE; + if (lastRet >= 0) { + if (!isDetached()) + removeAt(lastRet); + else { + final E lastItem = this.lastItem; + // assert lastItem != null; + this.lastItem = null; + if (itemAt(lastRet) == lastItem) + removeAt(lastRet); + } + } else if (lastRet == NONE) throw new IllegalStateException(); - lastRet = -1; - E x = lastItem; - lastItem = null; - // only remove if item still at index - if (x != null && x == items[i]) { - boolean removingHead = (i == takeIndex); - removeAt(i); - if (!removingHead) - nextIndex = dec(nextIndex); - } + // else lastRet == REMOVED and the last returned element was + // previously asynchronously removed via an operation other + // than this.remove(), so nothing to do. + + if (cursor < 0 && nextIndex < 0) + detach(); } finally { lock.unlock(); + // assert lastRet == NONE; + // assert lastItem == null; } } - } + /** + * Called to notify the iterator that the queue is empty, or that it + * has fallen hopelessly behind, so that it should abandon any + * further iteration, except possibly to return one more element + * from next(), as promised by returning true from hasNext(). + */ + void shutdown() { + // assert lock.getHoldCount() == 1; + cursor = NONE; + if (nextIndex >= 0) + nextIndex = REMOVED; + if (lastRet >= 0) { + lastRet = REMOVED; + lastItem = null; + } + prevTakeIndex = DETACHED; + // Don't set nextItem to null because we must continue to be + // able to return it on next(). + // + // Caller will unlink from itrs when convenient. + } + + private int distance(int index, int prevTakeIndex, int length) { + int distance = index - prevTakeIndex; + if (distance < 0) + distance += length; + return distance; + } + + /** + * Called whenever an interior remove (not at takeIndex) occured. + * + * @return true if this iterator should be unlinked from itrs + */ + boolean removedAt(int removedIndex) { + // assert lock.getHoldCount() == 1; + if (isDetached()) + return true; + + final int cycles = itrs.cycles; + final int takeIndex = ArrayBlockingQueue.this.takeIndex; + final int prevCycles = this.prevCycles; + final int prevTakeIndex = this.prevTakeIndex; + final int len = items.length; + int cycleDiff = cycles - prevCycles; + if (removedIndex < takeIndex) + cycleDiff++; + final int removedDistance = + (cycleDiff * len) + (removedIndex - prevTakeIndex); + // assert removedDistance >= 0; + int cursor = this.cursor; + if (cursor >= 0) { + int x = distance(cursor, prevTakeIndex, len); + if (x == removedDistance) { + if (cursor == putIndex) + this.cursor = cursor = NONE; + } + else if (x > removedDistance) { + // assert cursor != prevTakeIndex; + this.cursor = cursor = dec(cursor); + } + } + int lastRet = this.lastRet; + if (lastRet >= 0) { + int x = distance(lastRet, prevTakeIndex, len); + if (x == removedDistance) + this.lastRet = lastRet = REMOVED; + else if (x > removedDistance) + this.lastRet = lastRet = dec(lastRet); + } + int nextIndex = this.nextIndex; + if (nextIndex >= 0) { + int x = distance(nextIndex, prevTakeIndex, len); + if (x == removedDistance) + this.nextIndex = nextIndex = REMOVED; + else if (x > removedDistance) + this.nextIndex = nextIndex = dec(nextIndex); + } + else if (cursor < 0 && nextIndex < 0 && lastRet < 0) { + this.prevTakeIndex = DETACHED; + return true; + } + return false; + } + + /** + * Called whenever takeIndex wraps around to zero. + * + * @return true if this iterator should be unlinked from itrs + */ + boolean takeIndexWrapped() { + // assert lock.getHoldCount() == 1; + if (isDetached()) + return true; + if (itrs.cycles - prevCycles > 1) { + // All the elements that existed at the time of the last + // operation are gone, so abandon further iteration. + shutdown(); + return true; + } + return false; + } + +// /** Uncomment for debugging. */ +// public String toString() { +// return ("cursor=" + cursor + " " + +// "nextIndex=" + nextIndex + " " + +// "lastRet=" + lastRet + " " + +// "nextItem=" + nextItem + " " + +// "lastItem=" + lastItem + " " + +// "prevCycles=" + prevCycles + " " + +// "prevTakeIndex=" + prevTakeIndex + " " + +// "size()=" + size() + " " + +// "remainingCapacity()=" + remainingCapacity()); +// } + } } diff --git a/luni/src/main/java/java/util/concurrent/BlockingDeque.java b/luni/src/main/java/java/util/concurrent/BlockingDeque.java index 136df9c..34f103d 100644 --- a/luni/src/main/java/java/util/concurrent/BlockingDeque.java +++ b/luni/src/main/java/java/util/concurrent/BlockingDeque.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; @@ -36,9 +36,9 @@ import java.util.*; * <tr> * <td><b>Insert</b></td> * <td>{@link #addFirst addFirst(e)}</td> - * <td>{@link #offerFirst offerFirst(e)}</td> + * <td>{@link #offerFirst(Object) offerFirst(e)}</td> * <td>{@link #putFirst putFirst(e)}</td> - * <td>{@link #offerFirst offerFirst(e, time, unit)}</td> + * <td>{@link #offerFirst(Object, long, TimeUnit) offerFirst(e, time, unit)}</td> * </tr> * <tr> * <td><b>Remove</b></td> @@ -67,9 +67,9 @@ import java.util.*; * <tr> * <td><b>Insert</b></td> * <td>{@link #addLast addLast(e)}</td> - * <td>{@link #offerLast offerLast(e)}</td> + * <td>{@link #offerLast(Object) offerLast(e)}</td> * <td>{@link #putLast putLast(e)}</td> - * <td>{@link #offerLast offerLast(e, time, unit)}</td> + * <td>{@link #offerLast(Object, long, TimeUnit) offerLast(e, time, unit)}</td> * </tr> * <tr> * <td><b>Remove</b></td> @@ -106,20 +106,20 @@ import java.util.*; * <td ALIGN=CENTER COLSPAN = 2> <b>Insert</b></td> * </tr> * <tr> - * <td>{@link #add add(e)}</td> - * <td>{@link #addLast addLast(e)}</td> + * <td>{@link #add(Object) add(e)}</td> + * <td>{@link #addLast(Object) addLast(e)}</td> * </tr> * <tr> - * <td>{@link #offer offer(e)}</td> - * <td>{@link #offerLast offerLast(e)}</td> + * <td>{@link #offer(Object) offer(e)}</td> + * <td>{@link #offerLast(Object) offerLast(e)}</td> * </tr> * <tr> - * <td>{@link #put put(e)}</td> - * <td>{@link #putLast putLast(e)}</td> + * <td>{@link #put(Object) put(e)}</td> + * <td>{@link #putLast(Object) putLast(e)}</td> * </tr> * <tr> - * <td>{@link #offer offer(e, time, unit)}</td> - * <td>{@link #offerLast offerLast(e, time, unit)}</td> + * <td>{@link #offer(Object, long, TimeUnit) offer(e, time, unit)}</td> + * <td>{@link #offerLast(Object, long, TimeUnit) offerLast(e, time, unit)}</td> * </tr> * <tr> * <td ALIGN=CENTER COLSPAN = 2> <b>Remove</b></td> @@ -181,7 +181,7 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> { * possible to do so immediately without violating capacity restrictions, * throwing an <tt>IllegalStateException</tt> if no space is currently * available. When using a capacity-restricted deque, it is generally - * preferable to use {@link #offerFirst offerFirst}. + * preferable to use {@link #offerFirst(Object) offerFirst}. * * @param e the element to add * @throws IllegalStateException {@inheritDoc} @@ -196,7 +196,7 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> { * possible to do so immediately without violating capacity restrictions, * throwing an <tt>IllegalStateException</tt> if no space is currently * available. When using a capacity-restricted deque, it is generally - * preferable to use {@link #offerLast offerLast}. + * preferable to use {@link #offerLast(Object) offerLast}. * * @param e the element to add * @throws IllegalStateException {@inheritDoc} @@ -212,7 +212,7 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> { * returning <tt>true</tt> upon success and <tt>false</tt> if no space is * currently available. * When using a capacity-restricted deque, this method is generally - * preferable to the {@link #addFirst addFirst} method, which can + * preferable to the {@link #addFirst(Object) addFirst} method, which can * fail to insert an element only by throwing an exception. * * @param e the element to add @@ -228,7 +228,7 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> { * returning <tt>true</tt> upon success and <tt>false</tt> if no space is * currently available. * When using a capacity-restricted deque, this method is generally - * preferable to the {@link #addLast addLast} method, which can + * preferable to the {@link #addLast(Object) addLast} method, which can * fail to insert an element only by throwing an exception. * * @param e the element to add @@ -371,8 +371,10 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> { * @param o element to be removed from this deque, if present * @return <tt>true</tt> if an element was removed as a result of this call * @throws ClassCastException if the class of the specified element - * is incompatible with this deque (optional) - * @throws NullPointerException if the specified element is null (optional) + * is incompatible with this deque + * (<a href="../Collection.html#optional-restrictions">optional</a>) + * @throws NullPointerException if the specified element is null + * (<a href="../Collection.html#optional-restrictions">optional</a>) */ boolean removeFirstOccurrence(Object o); @@ -387,8 +389,10 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> { * @param o element to be removed from this deque, if present * @return <tt>true</tt> if an element was removed as a result of this call * @throws ClassCastException if the class of the specified element - * is incompatible with this deque (optional) - * @throws NullPointerException if the specified element is null (optional) + * is incompatible with this deque + * (<a href="../Collection.html#optional-restrictions">optional</a>) + * @throws NullPointerException if the specified element is null + * (<a href="../Collection.html#optional-restrictions">optional</a>) */ boolean removeLastOccurrence(Object o); @@ -401,9 +405,9 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> { * <tt>true</tt> upon success and throwing an * <tt>IllegalStateException</tt> if no space is currently available. * When using a capacity-restricted deque, it is generally preferable to - * use {@link #offer offer}. + * use {@link #offer(Object) offer}. * - * <p>This method is equivalent to {@link #addLast addLast}. + * <p>This method is equivalent to {@link #addLast(Object) addLast}. * * @param e the element to add * @throws IllegalStateException {@inheritDoc} @@ -424,7 +428,7 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> { * generally preferable to the {@link #add} method, which can fail to * insert an element only by throwing an exception. * - * <p>This method is equivalent to {@link #offerLast offerLast}. + * <p>This method is equivalent to {@link #offerLast(Object) offerLast}. * * @param e the element to add * @throws ClassCastException if the class of the specified element @@ -440,7 +444,7 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> { * (in other words, at the tail of this deque), waiting if necessary for * space to become available. * - * <p>This method is equivalent to {@link #putLast putLast}. + * <p>This method is equivalent to {@link #putLast(Object) putLast}. * * @param e the element to add * @throws InterruptedException {@inheritDoc} @@ -458,7 +462,7 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> { * specified wait time if necessary for space to become available. * * <p>This method is equivalent to - * {@link #offerLast offerLast}. + * {@link #offerLast(Object,long,TimeUnit) offerLast}. * * @param e the element to add * @return <tt>true</tt> if the element was added to this deque, else @@ -557,13 +561,15 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> { * (or equivalently, if this deque changed as a result of the call). * * <p>This method is equivalent to - * {@link #removeFirstOccurrence removeFirstOccurrence}. + * {@link #removeFirstOccurrence(Object) removeFirstOccurrence}. * * @param o element to be removed from this deque, if present * @return <tt>true</tt> if this deque changed as a result of the call * @throws ClassCastException if the class of the specified element - * is incompatible with this deque (optional) - * @throws NullPointerException if the specified element is null (optional) + * is incompatible with this deque + * (<a href="../Collection.html#optional-restrictions">optional</a>) + * @throws NullPointerException if the specified element is null + * (<a href="../Collection.html#optional-restrictions">optional</a>) */ boolean remove(Object o); @@ -575,8 +581,10 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> { * @param o object to be checked for containment in this deque * @return <tt>true</tt> if this deque contains the specified element * @throws ClassCastException if the class of the specified element - * is incompatible with this deque (optional) - * @throws NullPointerException if the specified element is null (optional) + * is incompatible with this deque + * (<a href="../Collection.html#optional-restrictions">optional</a>) + * @throws NullPointerException if the specified element is null + * (<a href="../Collection.html#optional-restrictions">optional</a>) */ public boolean contains(Object o); @@ -602,7 +610,7 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> { * words, inserts the element at the front of this deque unless it would * violate capacity restrictions. * - * <p>This method is equivalent to {@link #addFirst addFirst}. + * <p>This method is equivalent to {@link #addFirst(Object) addFirst}. * * @throws IllegalStateException {@inheritDoc} * @throws ClassCastException {@inheritDoc} diff --git a/luni/src/main/java/java/util/concurrent/BlockingQueue.java b/luni/src/main/java/java/util/concurrent/BlockingQueue.java index d01c097..6cfe52b 100644 --- a/luni/src/main/java/java/util/concurrent/BlockingQueue.java +++ b/luni/src/main/java/java/util/concurrent/BlockingQueue.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; @@ -42,7 +42,7 @@ import java.util.Queue; * <td>{@link #add add(e)}</td> * <td>{@link #offer offer(e)}</td> * <td>{@link #put put(e)}</td> - * <td>{@link #offer offer(e, time, unit)}</td> + * <td>{@link #offer(Object, long, TimeUnit) offer(e, time, unit)}</td> * </tr> * <tr> * <td><b>Remove</b></td> @@ -102,7 +102,7 @@ import java.util.Queue; * Usage example, based on a typical producer-consumer scenario. * Note that a <tt>BlockingQueue</tt> can safely be used with multiple * producers and multiple consumers. - * <pre> + * <pre> {@code * class Producer implements Runnable { * private final BlockingQueue queue; * Producer(BlockingQueue q) { queue = q; } @@ -135,8 +135,7 @@ import java.util.Queue; * new Thread(c1).start(); * new Thread(c2).start(); * } - * } - * </pre> + * }}</pre> * * <p>Memory consistency effects: As with other concurrent * collections, actions in a thread prior to placing an object into a @@ -156,7 +155,7 @@ public interface BlockingQueue<E> extends Queue<E> { * <tt>true</tt> upon success and throwing an * <tt>IllegalStateException</tt> if no space is currently available. * When using a capacity-restricted queue, it is generally preferable to - * use {@link #offer offer}. + * use {@link #offer(Object) offer}. * * @param e the element to add * @return <tt>true</tt> (as specified by {@link Collection#add}) @@ -274,8 +273,10 @@ public interface BlockingQueue<E> extends Queue<E> { * @param o element to be removed from this queue, if present * @return <tt>true</tt> if this queue changed as a result of the call * @throws ClassCastException if the class of the specified element - * is incompatible with this queue (optional) - * @throws NullPointerException if the specified element is null (optional) + * is incompatible with this queue + * (<a href="../Collection.html#optional-restrictions">optional</a>) + * @throws NullPointerException if the specified element is null + * (<a href="../Collection.html#optional-restrictions">optional</a>) */ boolean remove(Object o); @@ -287,8 +288,10 @@ public interface BlockingQueue<E> extends Queue<E> { * @param o object to be checked for containment in this queue * @return <tt>true</tt> if this queue contains the specified element * @throws ClassCastException if the class of the specified element - * is incompatible with this queue (optional) - * @throws NullPointerException if the specified element is null (optional) + * is incompatible with this queue + * (<a href="../Collection.html#optional-restrictions">optional</a>) + * @throws NullPointerException if the specified element is null + * (<a href="../Collection.html#optional-restrictions">optional</a>) */ public boolean contains(Object o); diff --git a/luni/src/main/java/java/util/concurrent/BrokenBarrierException.java b/luni/src/main/java/java/util/concurrent/BrokenBarrierException.java index 3f93fbb..76fae13 100644 --- a/luni/src/main/java/java/util/concurrent/BrokenBarrierException.java +++ b/luni/src/main/java/java/util/concurrent/BrokenBarrierException.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; diff --git a/luni/src/main/java/java/util/concurrent/Callable.java b/luni/src/main/java/java/util/concurrent/Callable.java index abc4d04..293544b 100644 --- a/luni/src/main/java/java/util/concurrent/Callable.java +++ b/luni/src/main/java/java/util/concurrent/Callable.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; diff --git a/luni/src/main/java/java/util/concurrent/CancellationException.java b/luni/src/main/java/java/util/concurrent/CancellationException.java index 2c29544..dc452e4 100644 --- a/luni/src/main/java/java/util/concurrent/CancellationException.java +++ b/luni/src/main/java/java/util/concurrent/CancellationException.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; diff --git a/luni/src/main/java/java/util/concurrent/CompletionService.java b/luni/src/main/java/java/util/concurrent/CompletionService.java index df9f719..7b0931c 100644 --- a/luni/src/main/java/java/util/concurrent/CompletionService.java +++ b/luni/src/main/java/java/util/concurrent/CompletionService.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentHashMap.java b/luni/src/main/java/java/util/concurrent/ConcurrentHashMap.java index c142e2a..c85a5cc 100644 --- a/luni/src/main/java/java/util/concurrent/ConcurrentHashMap.java +++ b/luni/src/main/java/java/util/concurrent/ConcurrentHashMap.java @@ -1,16 +1,13 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; import java.util.concurrent.locks.*; import java.util.*; import java.io.Serializable; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; // BEGIN android-note // removed link to collections framework docs @@ -76,7 +73,25 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> /* * The basic strategy is to subdivide the table among Segments, - * each of which itself is a concurrently readable hash table. + * each of which itself is a concurrently readable hash table. To + * reduce footprint, all but one segments are constructed only + * when first needed (see ensureSegment). To maintain visibility + * in the presence of lazy construction, accesses to segments as + * well as elements of segment's table must use volatile access, + * which is done via Unsafe within methods segmentAt etc + * below. These provide the functionality of AtomicReferenceArrays + * but reduce the levels of indirection. Additionally, + * volatile-writes of table elements and entry "next" fields + * within locked operations use the cheaper "lazySet" forms of + * writes (via putOrderedObject) because these writes are always + * followed by lock releases that maintain sequential consistency + * of table updates. + * + * Historical note: The previous version of this class relied + * heavily on "final" fields, which avoided some volatile reads at + * the expense of a large initial footprint. Some remnants of + * that design (including forced construction of segment 0) exist + * to ensure serialization compatibility. */ /* ---------------- Constants -------------- */ @@ -108,8 +123,15 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> static final int MAXIMUM_CAPACITY = 1 << 30; /** + * The minimum capacity for per-segment tables. Must be a power + * of two, at least two to avoid immediate resizing on next use + * after lazy construction. + */ + static final int MIN_SEGMENT_TABLE_CAPACITY = 2; + + /** * The maximum number of segments to allow; used to bound - * constructor arguments. + * constructor arguments. Must be power of two less than 1 << 24. */ static final int MAX_SEGMENTS = 1 << 16; // slightly conservative @@ -135,7 +157,7 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> final int segmentShift; /** - * The segments, each of which is a specialized hash table + * The segments, each of which is a specialized hash table. */ final Segment<K,V>[] segments; @@ -143,7 +165,66 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> transient Set<Map.Entry<K,V>> entrySet; transient Collection<V> values; - /* ---------------- Small Utilities -------------- */ + /** + * ConcurrentHashMap list entry. Note that this is never exported + * out as a user-visible Map.Entry. + */ + static final class HashEntry<K,V> { + final int hash; + final K key; + volatile V value; + volatile HashEntry<K,V> next; + + HashEntry(int hash, K key, V value, HashEntry<K,V> next) { + this.hash = hash; + this.key = key; + this.value = value; + this.next = next; + } + + /** + * Sets next field with volatile write semantics. (See above + * about use of putOrderedObject.) + */ + final void setNext(HashEntry<K,V> n) { + UNSAFE.putOrderedObject(this, nextOffset, n); + } + + // Unsafe mechanics + static final sun.misc.Unsafe UNSAFE; + static final long nextOffset; + static { + try { + UNSAFE = sun.misc.Unsafe.getUnsafe(); + Class<?> k = HashEntry.class; + nextOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("next")); + } catch (Exception e) { + throw new Error(e); + } + } + } + + /** + * Gets the ith element of given table (if nonnull) with volatile + * read semantics. Note: This is manually integrated into a few + * performance-sensitive methods to reduce call overhead. + */ + @SuppressWarnings("unchecked") + static final <K,V> HashEntry<K,V> entryAt(HashEntry<K,V>[] tab, int i) { + return (tab == null) ? null : + (HashEntry<K,V>) UNSAFE.getObjectVolatile + (tab, ((long)i << TSHIFT) + TBASE); + } + + /** + * Sets the ith element of given table, with volatile write + * semantics. (See above about use of putOrderedObject.) + */ + static final <K,V> void setEntryAt(HashEntry<K,V>[] tab, int i, + HashEntry<K,V> e) { + UNSAFE.putOrderedObject(tab, ((long)i << TSHIFT) + TBASE, e); + } /** * Applies a supplemental hash function to a given hashCode, which @@ -164,104 +245,67 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> } /** - * Returns the segment that should be used for key with given hash - * @param hash the hash code for the key - * @return the segment - */ - final Segment<K,V> segmentFor(int hash) { - return segments[(hash >>> segmentShift) & segmentMask]; - } - - /* ---------------- Inner Classes -------------- */ - - /** - * ConcurrentHashMap list entry. Note that this is never exported - * out as a user-visible Map.Entry. - * - * Because the value field is volatile, not final, it is legal wrt - * the Java Memory Model for an unsynchronized reader to see null - * instead of initial value when read via a data race. Although a - * reordering leading to this is not likely to ever actually - * occur, the Segment.readValueUnderLock method is used as a - * backup in case a null (pre-initialized) value is ever seen in - * an unsynchronized access method. - */ - static final class HashEntry<K,V> { - final K key; - final int hash; - volatile V value; - final HashEntry<K,V> next; - - HashEntry(K key, int hash, HashEntry<K,V> next, V value) { - this.key = key; - this.hash = hash; - this.next = next; - this.value = value; - } - - @SuppressWarnings("unchecked") - static final <K,V> HashEntry<K,V>[] newArray(int i) { - return new HashEntry[i]; - } - } - - /** * Segments are specialized versions of hash tables. This * subclasses from ReentrantLock opportunistically, just to * simplify some locking and avoid separate construction. */ static final class Segment<K,V> extends ReentrantLock implements Serializable { /* - * Segments maintain a table of entry lists that are ALWAYS - * kept in a consistent state, so can be read without locking. - * Next fields of nodes are immutable (final). All list - * additions are performed at the front of each bin. This - * makes it easy to check changes, and also fast to traverse. - * When nodes would otherwise be changed, new nodes are - * created to replace them. This works well for hash tables - * since the bin lists tend to be short. (The average length - * is less than two for the default load factor threshold.) - * - * Read operations can thus proceed without locking, but rely - * on selected uses of volatiles to ensure that completed - * write operations performed by other threads are - * noticed. For most purposes, the "count" field, tracking the - * number of elements, serves as that volatile variable - * ensuring visibility. This is convenient because this field - * needs to be read in many read operations anyway: - * - * - All (unsynchronized) read operations must first read the - * "count" field, and should not look at table entries if - * it is 0. + * Segments maintain a table of entry lists that are always + * kept in a consistent state, so can be read (via volatile + * reads of segments and tables) without locking. This + * requires replicating nodes when necessary during table + * resizing, so the old lists can be traversed by readers + * still using old version of table. * - * - All (synchronized) write operations should write to - * the "count" field after structurally changing any bin. - * The operations must not take any action that could even - * momentarily cause a concurrent read operation to see - * inconsistent data. This is made easier by the nature of - * the read operations in Map. For example, no operation - * can reveal that the table has grown but the threshold - * has not yet been updated, so there are no atomicity - * requirements for this with respect to reads. - * - * As a guide, all critical volatile reads and writes to the - * count field are marked in code comments. + * This class defines only mutative methods requiring locking. + * Except as noted, the methods of this class perform the + * per-segment versions of ConcurrentHashMap methods. (Other + * methods are integrated directly into ConcurrentHashMap + * methods.) These mutative methods use a form of controlled + * spinning on contention via methods scanAndLock and + * scanAndLockForPut. These intersperse tryLocks with + * traversals to locate nodes. The main benefit is to absorb + * cache misses (which are very common for hash tables) while + * obtaining locks so that traversal is faster once + * acquired. We do not actually use the found nodes since they + * must be re-acquired under lock anyway to ensure sequential + * consistency of updates (and in any case may be undetectably + * stale), but they will normally be much faster to re-locate. + * Also, scanAndLockForPut speculatively creates a fresh node + * to use in put if no node is found. */ private static final long serialVersionUID = 2249069246763182397L; /** - * The number of elements in this segment's region. + * The maximum number of times to tryLock in a prescan before + * possibly blocking on acquire in preparation for a locked + * segment operation. On multiprocessors, using a bounded + * number of retries maintains cache acquired while locating + * nodes. */ - transient volatile int count; + static final int MAX_SCAN_RETRIES = + Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1; + + /** + * The per-segment table. Elements are accessed via + * entryAt/setEntryAt providing volatile semantics. + */ + transient volatile HashEntry<K,V>[] table; /** - * Number of updates that alter the size of the table. This is - * used during bulk-read methods to make sure they see a - * consistent snapshot: If modCounts change during a traversal - * of segments computing size or checking containsValue, then - * we might have an inconsistent view of state so (usually) - * must retry. + * The number of elements. Accessed only either within locks + * or among other volatile reads that maintain visibility. + */ + transient int count; + + /** + * The total number of mutative operations in this segment. + * Even though this may overflows 32 bits, it provides + * sufficient accuracy for stability checks in CHM isEmpty() + * and size() methods. Accessed only either within locks or + * among other volatile reads that maintain visibility. */ transient int modCount; @@ -273,11 +317,6 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> transient int threshold; /** - * The per-segment table. - */ - transient volatile HashEntry<K,V>[] table; - - /** * The load factor for the hash table. Even though this value * is same for all segments, it is replicated to avoid needing * links to outer object. @@ -285,202 +324,93 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> */ final float loadFactor; - Segment(int initialCapacity, float lf) { - loadFactor = lf; - setTable(HashEntry.<K,V>newArray(initialCapacity)); - } - - @SuppressWarnings("unchecked") - static final <K,V> Segment<K,V>[] newArray(int i) { - return new Segment[i]; - } - - /** - * Sets table to new HashEntry array. - * Call only while holding lock or in constructor. - */ - void setTable(HashEntry<K,V>[] newTable) { - threshold = (int)(newTable.length * loadFactor); - table = newTable; - } - - /** - * Returns properly casted first entry of bin for given hash. - */ - HashEntry<K,V> getFirst(int hash) { - HashEntry<K,V>[] tab = table; - return tab[hash & (tab.length - 1)]; + Segment(float lf, int threshold, HashEntry<K,V>[] tab) { + this.loadFactor = lf; + this.threshold = threshold; + this.table = tab; } - /** - * Reads value field of an entry under lock. Called if value - * field ever appears to be null. This is possible only if a - * compiler happens to reorder a HashEntry initialization with - * its table assignment, which is legal under memory model - * but is not known to ever occur. - */ - V readValueUnderLock(HashEntry<K,V> e) { - lock(); + final V put(K key, int hash, V value, boolean onlyIfAbsent) { + HashEntry<K,V> node = tryLock() ? null : + scanAndLockForPut(key, hash, value); + V oldValue; try { - return e.value; - } finally { - unlock(); - } - } - - /* Specialized implementations of map methods */ - - V get(Object key, int hash) { - if (count != 0) { // read-volatile - HashEntry<K,V> e = getFirst(hash); - while (e != null) { - if (e.hash == hash && key.equals(e.key)) { - V v = e.value; - if (v != null) - return v; - return readValueUnderLock(e); // recheck - } - e = e.next; - } - } - return null; - } - - boolean containsKey(Object key, int hash) { - if (count != 0) { // read-volatile - HashEntry<K,V> e = getFirst(hash); - while (e != null) { - if (e.hash == hash && key.equals(e.key)) - return true; - e = e.next; - } - } - return false; - } - - boolean containsValue(Object value) { - if (count != 0) { // read-volatile HashEntry<K,V>[] tab = table; - int len = tab.length; - for (int i = 0 ; i < len; i++) { - for (HashEntry<K,V> e = tab[i]; e != null; e = e.next) { - V v = e.value; - if (v == null) // recheck - v = readValueUnderLock(e); - if (value.equals(v)) - return true; + int index = (tab.length - 1) & hash; + HashEntry<K,V> first = entryAt(tab, index); + for (HashEntry<K,V> e = first;;) { + if (e != null) { + K k; + if ((k = e.key) == key || + (e.hash == hash && key.equals(k))) { + oldValue = e.value; + if (!onlyIfAbsent) { + e.value = value; + ++modCount; + } + break; + } + e = e.next; + } + else { + if (node != null) + node.setNext(first); + else + node = new HashEntry<K,V>(hash, key, value, first); + int c = count + 1; + if (c > threshold && tab.length < MAXIMUM_CAPACITY) + rehash(node); + else + setEntryAt(tab, index, node); + ++modCount; + count = c; + oldValue = null; + break; } } - } - return false; - } - - boolean replace(K key, int hash, V oldValue, V newValue) { - lock(); - try { - HashEntry<K,V> e = getFirst(hash); - while (e != null && (e.hash != hash || !key.equals(e.key))) - e = e.next; - - boolean replaced = false; - if (e != null && oldValue.equals(e.value)) { - replaced = true; - e.value = newValue; - } - return replaced; - } finally { - unlock(); - } - } - - V replace(K key, int hash, V newValue) { - lock(); - try { - HashEntry<K,V> e = getFirst(hash); - while (e != null && (e.hash != hash || !key.equals(e.key))) - e = e.next; - - V oldValue = null; - if (e != null) { - oldValue = e.value; - e.value = newValue; - } - return oldValue; - } finally { - unlock(); - } - } - - - V put(K key, int hash, V value, boolean onlyIfAbsent) { - lock(); - try { - int c = count; - if (c++ > threshold) // ensure capacity - rehash(); - HashEntry<K,V>[] tab = table; - int index = hash & (tab.length - 1); - HashEntry<K,V> first = tab[index]; - HashEntry<K,V> e = first; - while (e != null && (e.hash != hash || !key.equals(e.key))) - e = e.next; - - V oldValue; - if (e != null) { - oldValue = e.value; - if (!onlyIfAbsent) - e.value = value; - } - else { - oldValue = null; - ++modCount; - tab[index] = new HashEntry<K,V>(key, hash, first, value); - count = c; // write-volatile - } - return oldValue; } finally { unlock(); } + return oldValue; } - void rehash() { - HashEntry<K,V>[] oldTable = table; - int oldCapacity = oldTable.length; - if (oldCapacity >= MAXIMUM_CAPACITY) - return; - + /** + * Doubles size of table and repacks entries, also adding the + * given node to new table + */ + @SuppressWarnings("unchecked") + private void rehash(HashEntry<K,V> node) { /* - * Reclassify nodes in each list to new Map. Because we are - * using power-of-two expansion, the elements from each bin - * must either stay at same index, or move with a power of two - * offset. We eliminate unnecessary node creation by catching - * cases where old nodes can be reused because their next - * fields won't change. Statistically, at the default - * threshold, only about one-sixth of them need cloning when - * a table doubles. The nodes they replace will be garbage - * collectable as soon as they are no longer referenced by any - * reader thread that may be in the midst of traversing table - * right now. + * Reclassify nodes in each list to new table. Because we + * are using power-of-two expansion, the elements from + * each bin must either stay at same index, or move with a + * power of two offset. We eliminate unnecessary node + * creation by catching cases where old nodes can be + * reused because their next fields won't change. + * Statistically, at the default threshold, only about + * one-sixth of them need cloning when a table + * doubles. The nodes they replace will be garbage + * collectable as soon as they are no longer referenced by + * any reader thread that may be in the midst of + * concurrently traversing table. Entry accesses use plain + * array indexing because they are followed by volatile + * table write. */ - - HashEntry<K,V>[] newTable = HashEntry.newArray(oldCapacity<<1); - threshold = (int)(newTable.length * loadFactor); - int sizeMask = newTable.length - 1; + HashEntry<K,V>[] oldTable = table; + int oldCapacity = oldTable.length; + int newCapacity = oldCapacity << 1; + threshold = (int)(newCapacity * loadFactor); + HashEntry<K,V>[] newTable = + (HashEntry<K,V>[]) new HashEntry<?,?>[newCapacity]; + int sizeMask = newCapacity - 1; for (int i = 0; i < oldCapacity ; i++) { - // We need to guarantee that any existing reads of old Map can - // proceed. So we cannot yet null out each bin. HashEntry<K,V> e = oldTable[i]; - if (e != null) { HashEntry<K,V> next = e.next; int idx = e.hash & sizeMask; - - // Single node on list - if (next == null) + if (next == null) // Single node on list newTable[idx] = e; - - else { - // Reuse trailing consecutive sequence at same slot + else { // Reuse consecutive sequence at same slot HashEntry<K,V> lastRun = e; int lastIdx = idx; for (HashEntry<K,V> last = next; @@ -493,74 +423,263 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> } } newTable[lastIdx] = lastRun; - - // Clone all remaining nodes + // Clone remaining nodes for (HashEntry<K,V> p = e; p != lastRun; p = p.next) { - int k = p.hash & sizeMask; + V v = p.value; + int h = p.hash; + int k = h & sizeMask; HashEntry<K,V> n = newTable[k]; - newTable[k] = new HashEntry<K,V>(p.key, p.hash, - n, p.value); + newTable[k] = new HashEntry<K,V>(h, p.key, v, n); } } } } + int nodeIndex = node.hash & sizeMask; // add the new node + node.setNext(newTable[nodeIndex]); + newTable[nodeIndex] = node; table = newTable; } /** + * Scans for a node containing given key while trying to + * acquire lock, creating and returning one if not found. Upon + * return, guarantees that lock is held. Unlike in most + * methods, calls to method equals are not screened: Since + * traversal speed doesn't matter, we might as well help warm + * up the associated code and accesses as well. + * + * @return a new node if key not found, else null + */ + private HashEntry<K,V> scanAndLockForPut(K key, int hash, V value) { + HashEntry<K,V> first = entryForHash(this, hash); + HashEntry<K,V> e = first; + HashEntry<K,V> node = null; + int retries = -1; // negative while locating node + while (!tryLock()) { + HashEntry<K,V> f; // to recheck first below + if (retries < 0) { + if (e == null) { + if (node == null) // speculatively create node + node = new HashEntry<K,V>(hash, key, value, null); + retries = 0; + } + else if (key.equals(e.key)) + retries = 0; + else + e = e.next; + } + else if (++retries > MAX_SCAN_RETRIES) { + lock(); + break; + } + else if ((retries & 1) == 0 && + (f = entryForHash(this, hash)) != first) { + e = first = f; // re-traverse if entry changed + retries = -1; + } + } + return node; + } + + /** + * Scans for a node containing the given key while trying to + * acquire lock for a remove or replace operation. Upon + * return, guarantees that lock is held. Note that we must + * lock even if the key is not found, to ensure sequential + * consistency of updates. + */ + private void scanAndLock(Object key, int hash) { + // similar to but simpler than scanAndLockForPut + HashEntry<K,V> first = entryForHash(this, hash); + HashEntry<K,V> e = first; + int retries = -1; + while (!tryLock()) { + HashEntry<K,V> f; + if (retries < 0) { + if (e == null || key.equals(e.key)) + retries = 0; + else + e = e.next; + } + else if (++retries > MAX_SCAN_RETRIES) { + lock(); + break; + } + else if ((retries & 1) == 0 && + (f = entryForHash(this, hash)) != first) { + e = first = f; + retries = -1; + } + } + } + + /** * Remove; match on key only if value null, else match both. */ - V remove(Object key, int hash, Object value) { - lock(); + final V remove(Object key, int hash, Object value) { + if (!tryLock()) + scanAndLock(key, hash); + V oldValue = null; try { - int c = count - 1; HashEntry<K,V>[] tab = table; - int index = hash & (tab.length - 1); - HashEntry<K,V> first = tab[index]; - HashEntry<K,V> e = first; - while (e != null && (e.hash != hash || !key.equals(e.key))) - e = e.next; + int index = (tab.length - 1) & hash; + HashEntry<K,V> e = entryAt(tab, index); + HashEntry<K,V> pred = null; + while (e != null) { + K k; + HashEntry<K,V> next = e.next; + if ((k = e.key) == key || + (e.hash == hash && key.equals(k))) { + V v = e.value; + if (value == null || value == v || value.equals(v)) { + if (pred == null) + setEntryAt(tab, index, next); + else + pred.setNext(next); + ++modCount; + --count; + oldValue = v; + } + break; + } + pred = e; + e = next; + } + } finally { + unlock(); + } + return oldValue; + } - V oldValue = null; - if (e != null) { - V v = e.value; - if (value == null || value.equals(v)) { - oldValue = v; - // All entries following removed node can stay - // in list, but all preceding ones need to be - // cloned. + final boolean replace(K key, int hash, V oldValue, V newValue) { + if (!tryLock()) + scanAndLock(key, hash); + boolean replaced = false; + try { + HashEntry<K,V> e; + for (e = entryForHash(this, hash); e != null; e = e.next) { + K k; + if ((k = e.key) == key || + (e.hash == hash && key.equals(k))) { + if (oldValue.equals(e.value)) { + e.value = newValue; + ++modCount; + replaced = true; + } + break; + } + } + } finally { + unlock(); + } + return replaced; + } + + final V replace(K key, int hash, V value) { + if (!tryLock()) + scanAndLock(key, hash); + V oldValue = null; + try { + HashEntry<K,V> e; + for (e = entryForHash(this, hash); e != null; e = e.next) { + K k; + if ((k = e.key) == key || + (e.hash == hash && key.equals(k))) { + oldValue = e.value; + e.value = value; ++modCount; - HashEntry<K,V> newFirst = e.next; - for (HashEntry<K,V> p = first; p != e; p = p.next) - newFirst = new HashEntry<K,V>(p.key, p.hash, - newFirst, p.value); - tab[index] = newFirst; - count = c; // write-volatile + break; } } - return oldValue; + } finally { + unlock(); + } + return oldValue; + } + + final void clear() { + lock(); + try { + HashEntry<K,V>[] tab = table; + for (int i = 0; i < tab.length ; i++) + setEntryAt(tab, i, null); + ++modCount; + count = 0; } finally { unlock(); } } + } + + // Accessing segments + + /** + * Gets the jth element of given segment array (if nonnull) with + * volatile element access semantics via Unsafe. (The null check + * can trigger harmlessly only during deserialization.) Note: + * because each element of segments array is set only once (using + * fully ordered writes), some performance-sensitive methods rely + * on this method only as a recheck upon null reads. + */ + @SuppressWarnings("unchecked") + static final <K,V> Segment<K,V> segmentAt(Segment<K,V>[] ss, int j) { + long u = (j << SSHIFT) + SBASE; + return ss == null ? null : + (Segment<K,V>) UNSAFE.getObjectVolatile(ss, u); + } - void clear() { - if (count != 0) { - lock(); - try { - HashEntry<K,V>[] tab = table; - for (int i = 0; i < tab.length ; i++) - tab[i] = null; - ++modCount; - count = 0; // write-volatile - } finally { - unlock(); + /** + * Returns the segment for the given index, creating it and + * recording in segment table (via CAS) if not already present. + * + * @param k the index + * @return the segment + */ + @SuppressWarnings("unchecked") + private Segment<K,V> ensureSegment(int k) { + final Segment<K,V>[] ss = this.segments; + long u = (k << SSHIFT) + SBASE; // raw offset + Segment<K,V> seg; + if ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u)) == null) { + Segment<K,V> proto = ss[0]; // use segment 0 as prototype + int cap = proto.table.length; + float lf = proto.loadFactor; + int threshold = (int)(cap * lf); + HashEntry<K,V>[] tab = (HashEntry<K,V>[])new HashEntry<?,?>[cap]; + if ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u)) + == null) { // recheck + Segment<K,V> s = new Segment<K,V>(lf, threshold, tab); + while ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u)) + == null) { + if (UNSAFE.compareAndSwapObject(ss, u, null, seg = s)) + break; } } } + return seg; } + // Hash-based segment and entry accesses + /** + * Gets the segment for the given hash code. + */ + @SuppressWarnings("unchecked") + private Segment<K,V> segmentForHash(int h) { + long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE; + return (Segment<K,V>) UNSAFE.getObjectVolatile(segments, u); + } + + /** + * Gets the table entry for the given segment and hash code. + */ + @SuppressWarnings("unchecked") + static final <K,V> HashEntry<K,V> entryForHash(Segment<K,V> seg, int h) { + HashEntry<K,V>[] tab; + return (seg == null || (tab = seg.table) == null) ? null : + (HashEntry<K,V>) UNSAFE.getObjectVolatile + (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE); + } /* ---------------- Public operations -------------- */ @@ -580,14 +699,13 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> * negative or the load factor or concurrencyLevel are * nonpositive. */ + @SuppressWarnings("unchecked") public ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) { if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0) throw new IllegalArgumentException(); - if (concurrencyLevel > MAX_SEGMENTS) concurrencyLevel = MAX_SEGMENTS; - // Find power-of-two sizes best matching arguments int sshift = 0; int ssize = 1; @@ -595,21 +713,23 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> ++sshift; ssize <<= 1; } - segmentShift = 32 - sshift; - segmentMask = ssize - 1; - this.segments = Segment.newArray(ssize); - + this.segmentShift = 32 - sshift; + this.segmentMask = ssize - 1; if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; int c = initialCapacity / ssize; if (c * ssize < initialCapacity) ++c; - int cap = 1; + int cap = MIN_SEGMENT_TABLE_CAPACITY; while (cap < c) cap <<= 1; - - for (int i = 0; i < this.segments.length; ++i) - this.segments[i] = new Segment<K,V>(cap, loadFactor); + // create segments and segments[0] + Segment<K,V> s0 = + new Segment<K,V>(loadFactor, (int)(cap * loadFactor), + (HashEntry<K,V>[])new HashEntry<?,?>[cap]); + Segment<K,V>[] ss = (Segment<K,V>[])new Segment<?,?>[ssize]; + UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0] + this.segments = ss; } /** @@ -672,33 +792,36 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> * @return <tt>true</tt> if this map contains no key-value mappings */ public boolean isEmpty() { - final Segment<K,V>[] segments = this.segments; /* - * We keep track of per-segment modCounts to avoid ABA - * problems in which an element in one segment was added and - * in another removed during traversal, in which case the - * table was never actually empty at any point. Note the - * similar use of modCounts in the size() and containsValue() - * methods, which are the only other methods also susceptible - * to ABA problems. + * Sum per-segment modCounts to avoid mis-reporting when + * elements are concurrently added and removed in one segment + * while checking another, in which case the table was never + * actually empty at any point. (The sum ensures accuracy up + * through at least 1<<31 per-segment modifications before + * recheck.) Methods size() and containsValue() use similar + * constructions for stability checks. */ - int[] mc = new int[segments.length]; - int mcsum = 0; - for (int i = 0; i < segments.length; ++i) { - if (segments[i].count != 0) - return false; - else - mcsum += mc[i] = segments[i].modCount; - } - // If mcsum happens to be zero, then we know we got a snapshot - // before any modifications at all were made. This is - // probably common enough to bother tracking. - if (mcsum != 0) { - for (int i = 0; i < segments.length; ++i) { - if (segments[i].count != 0 || - mc[i] != segments[i].modCount) + long sum = 0L; + final Segment<K,V>[] segments = this.segments; + for (int j = 0; j < segments.length; ++j) { + Segment<K,V> seg = segmentAt(segments, j); + if (seg != null) { + if (seg.count != 0) return false; + sum += seg.modCount; + } + } + if (sum != 0L) { // recheck unless no modifications + for (int j = 0; j < segments.length; ++j) { + Segment<K,V> seg = segmentAt(segments, j); + if (seg != null) { + if (seg.count != 0) + return false; + sum -= seg.modCount; + } } + if (sum != 0L) + return false; } return true; } @@ -711,45 +834,36 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> * @return the number of key-value mappings in this map */ public int size() { - final Segment<K,V>[] segments = this.segments; - long sum = 0; - long check = 0; - int[] mc = new int[segments.length]; // Try a few times to get accurate count. On failure due to // continuous async changes in table, resort to locking. - for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) { - check = 0; - sum = 0; - int mcsum = 0; - for (int i = 0; i < segments.length; ++i) { - sum += segments[i].count; - mcsum += mc[i] = segments[i].modCount; - } - if (mcsum != 0) { - for (int i = 0; i < segments.length; ++i) { - check += segments[i].count; - if (mc[i] != segments[i].modCount) { - check = -1; // force retry - break; - } + final Segment<K,V>[] segments = this.segments; + final int segmentCount = segments.length; + + long previousSum = 0L; + for (int retries = -1; retries < RETRIES_BEFORE_LOCK; retries++) { + long sum = 0L; // sum of modCounts + long size = 0L; + for (int i = 0; i < segmentCount; i++) { + Segment<K,V> segment = segmentAt(segments, i); + if (segment != null) { + sum += segment.modCount; + size += segment.count; } } - if (check == sum) - break; + if (sum == previousSum) + return ((size >>> 31) == 0) ? (int) size : Integer.MAX_VALUE; + previousSum = sum; } - if (check != sum) { // Resort to locking all segments - sum = 0; - for (int i = 0; i < segments.length; ++i) - segments[i].lock(); - for (int i = 0; i < segments.length; ++i) - sum += segments[i].count; - for (int i = 0; i < segments.length; ++i) - segments[i].unlock(); + + long size = 0L; + for (int i = 0; i < segmentCount; i++) { + Segment<K,V> segment = ensureSegment(i); + segment.lock(); + size += segment.count; } - if (sum > Integer.MAX_VALUE) - return Integer.MAX_VALUE; - else - return (int)sum; + for (int i = 0; i < segmentCount; i++) + segments[i].unlock(); + return ((size >>> 31) == 0) ? (int) size : Integer.MAX_VALUE; } /** @@ -764,8 +878,21 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> * @throws NullPointerException if the specified key is null */ public V get(Object key) { - int hash = hash(key.hashCode()); - return segmentFor(hash).get(key, hash); + Segment<K,V> s; // manually integrate access methods to reduce overhead + HashEntry<K,V>[] tab; + int h = hash(key.hashCode()); + long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE; + if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null && + (tab = s.table) != null) { + for (HashEntry<K,V> e = (HashEntry<K,V>) UNSAFE.getObjectVolatile + (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE); + e != null; e = e.next) { + K k; + if ((k = e.key) == key || (e.hash == h && key.equals(k))) + return e.value; + } + } + return null; } /** @@ -777,9 +904,23 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> * <tt>equals</tt> method; <tt>false</tt> otherwise. * @throws NullPointerException if the specified key is null */ + @SuppressWarnings("unchecked") public boolean containsKey(Object key) { - int hash = hash(key.hashCode()); - return segmentFor(hash).containsKey(key, hash); + Segment<K,V> s; // same as get() except no need for volatile value read + HashEntry<K,V>[] tab; + int h = hash(key.hashCode()); + long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE; + if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null && + (tab = s.table) != null) { + for (HashEntry<K,V> e = (HashEntry<K,V>) UNSAFE.getObjectVolatile + (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE); + e != null; e = e.next) { + K k; + if ((k = e.key) == key || (e.hash == h && key.equals(k))) + return true; + } + } + return false; } /** @@ -794,53 +935,47 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> * @throws NullPointerException if the specified value is null */ public boolean containsValue(Object value) { + // Same idea as size() if (value == null) throw new NullPointerException(); - - // See explanation of modCount use above - final Segment<K,V>[] segments = this.segments; - int[] mc = new int[segments.length]; - - // Try a few times without locking - for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) { - int sum = 0; - int mcsum = 0; - for (int i = 0; i < segments.length; ++i) { - int c = segments[i].count; - mcsum += mc[i] = segments[i].modCount; - if (segments[i].containsValue(value)) - return true; - } - boolean cleanSweep = true; - if (mcsum != 0) { - for (int i = 0; i < segments.length; ++i) { - int c = segments[i].count; - if (mc[i] != segments[i].modCount) { - cleanSweep = false; - break; - } - } - } - if (cleanSweep) - return false; - } - // Resort to locking all segments - for (int i = 0; i < segments.length; ++i) - segments[i].lock(); - boolean found = false; + long previousSum = 0L; + int lockCount = 0; try { - for (int i = 0; i < segments.length; ++i) { - if (segments[i].containsValue(value)) { - found = true; - break; + for (int retries = -1; ; retries++) { + long sum = 0L; // sum of modCounts + for (int j = 0; j < segments.length; j++) { + Segment<K,V> segment; + if (retries == RETRIES_BEFORE_LOCK) { + segment = ensureSegment(j); + segment.lock(); + lockCount++; + } else { + segment = segmentAt(segments, j); + if (segment == null) + continue; + } + HashEntry<K,V>[] tab = segment.table; + if (tab != null) { + for (int i = 0 ; i < tab.length; i++) { + HashEntry<K,V> e; + for (e = entryAt(tab, i); e != null; e = e.next) { + V v = e.value; + if (v != null && value.equals(v)) + return true; + } + } + sum += segment.modCount; + } } + if ((retries >= 0 && sum == previousSum) || lockCount > 0) + return false; + previousSum = sum; } } finally { - for (int i = 0; i < segments.length; ++i) - segments[i].unlock(); + for (int j = 0; j < lockCount; j++) + segments[j].unlock(); } - return found; } /** @@ -850,7 +985,7 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> * full compatibility with class {@link java.util.Hashtable}, * which supported this method prior to introduction of the * Java Collections framework. - + * * @param value a value to search for * @return <tt>true</tt> if and only if some key maps to the * <tt>value</tt> argument in this table as @@ -875,11 +1010,17 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> * <tt>null</tt> if there was no mapping for <tt>key</tt> * @throws NullPointerException if the specified key or value is null */ + @SuppressWarnings("unchecked") public V put(K key, V value) { + Segment<K,V> s; if (value == null) throw new NullPointerException(); int hash = hash(key.hashCode()); - return segmentFor(hash).put(key, hash, value, false); + int j = (hash >>> segmentShift) & segmentMask; + if ((s = (Segment<K,V>)UNSAFE.getObject // nonvolatile; recheck + (segments, (j << SSHIFT) + SBASE)) == null) // in ensureSegment + s = ensureSegment(j); + return s.put(key, hash, value, false); } /** @@ -889,11 +1030,17 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> * or <tt>null</tt> if there was no mapping for the key * @throws NullPointerException if the specified key or value is null */ + @SuppressWarnings("unchecked") public V putIfAbsent(K key, V value) { + Segment<K,V> s; if (value == null) throw new NullPointerException(); int hash = hash(key.hashCode()); - return segmentFor(hash).put(key, hash, value, true); + int j = (hash >>> segmentShift) & segmentMask; + if ((s = (Segment<K,V>)UNSAFE.getObject + (segments, (j << SSHIFT) + SBASE)) == null) + s = ensureSegment(j); + return s.put(key, hash, value, true); } /** @@ -919,7 +1066,8 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> */ public V remove(Object key) { int hash = hash(key.hashCode()); - return segmentFor(hash).remove(key, hash, null); + Segment<K,V> s = segmentForHash(hash); + return s == null ? null : s.remove(key, hash, null); } /** @@ -929,9 +1077,9 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> */ public boolean remove(Object key, Object value) { int hash = hash(key.hashCode()); - if (value == null) - return false; - return segmentFor(hash).remove(key, hash, value) != null; + Segment<K,V> s; + return value != null && (s = segmentForHash(hash)) != null && + s.remove(key, hash, value) != null; } /** @@ -940,10 +1088,11 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> * @throws NullPointerException if any of the arguments are null */ public boolean replace(K key, V oldValue, V newValue) { + int hash = hash(key.hashCode()); if (oldValue == null || newValue == null) throw new NullPointerException(); - int hash = hash(key.hashCode()); - return segmentFor(hash).replace(key, hash, oldValue, newValue); + Segment<K,V> s = segmentForHash(hash); + return s != null && s.replace(key, hash, oldValue, newValue); } /** @@ -954,18 +1103,23 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> * @throws NullPointerException if the specified key or value is null */ public V replace(K key, V value) { + int hash = hash(key.hashCode()); if (value == null) throw new NullPointerException(); - int hash = hash(key.hashCode()); - return segmentFor(hash).replace(key, hash, value); + Segment<K,V> s = segmentForHash(hash); + return s == null ? null : s.replace(key, hash, value); } /** * Removes all of the mappings from this map. */ public void clear() { - for (int i = 0; i < segments.length; ++i) - segments[i].clear(); + final Segment<K,V>[] segments = this.segments; + for (int j = 0; j < segments.length; ++j) { + Segment<K,V> s = segmentAt(segments, j); + if (s != null) + s.clear(); + } } /** @@ -1066,42 +1220,41 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> advance(); } - public boolean hasMoreElements() { return hasNext(); } - + /** + * Sets nextEntry to first node of next non-empty table + * (in backwards order, to simplify checks). + */ final void advance() { - if (nextEntry != null && (nextEntry = nextEntry.next) != null) - return; - - while (nextTableIndex >= 0) { - if ( (nextEntry = currentTable[nextTableIndex--]) != null) - return; - } - - while (nextSegmentIndex >= 0) { - Segment<K,V> seg = segments[nextSegmentIndex--]; - if (seg.count != 0) { - currentTable = seg.table; - for (int j = currentTable.length - 1; j >= 0; --j) { - if ( (nextEntry = currentTable[j]) != null) { - nextTableIndex = j - 1; - return; - } - } + for (;;) { + if (nextTableIndex >= 0) { + if ((nextEntry = entryAt(currentTable, + nextTableIndex--)) != null) + break; + } + else if (nextSegmentIndex >= 0) { + Segment<K,V> seg = segmentAt(segments, nextSegmentIndex--); + if (seg != null && (currentTable = seg.table) != null) + nextTableIndex = currentTable.length - 1; } + else + break; } } - public boolean hasNext() { return nextEntry != null; } - - HashEntry<K,V> nextEntry() { - if (nextEntry == null) + final HashEntry<K,V> nextEntry() { + HashEntry<K,V> e = nextEntry; + if (e == null) throw new NoSuchElementException(); - lastReturned = nextEntry; - advance(); - return lastReturned; + lastReturned = e; // cannot assign until after null check + if ((nextEntry = e.next) == null) + advance(); + return e; } - public void remove() { + public final boolean hasNext() { return nextEntry != null; } + public final boolean hasMoreElements() { return nextEntry != null; } + + public final void remove() { if (lastReturned == null) throw new IllegalStateException(); ConcurrentHashMap.this.remove(lastReturned.key); @@ -1113,16 +1266,16 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> extends HashIterator implements Iterator<K>, Enumeration<K> { - public K next() { return super.nextEntry().key; } - public K nextElement() { return super.nextEntry().key; } + public final K next() { return super.nextEntry().key; } + public final K nextElement() { return super.nextEntry().key; } } final class ValueIterator extends HashIterator implements Iterator<V>, Enumeration<V> { - public V next() { return super.nextEntry().value; } - public V nextElement() { return super.nextEntry().value; } + public final V next() { return super.nextEntry().value; } + public final V nextElement() { return super.nextEntry().value; } } /** @@ -1137,7 +1290,7 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> } /** - * Set our entry's value and write through to the map. The + * Sets our entry's value and writes through to the map. The * value to return is somewhat arbitrary here. Since a * WriteThroughEntry does not necessarily track asynchronous * changes, the most recent "previous" value could be @@ -1233,24 +1386,30 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> /* ---------------- Serialization Support -------------- */ /** - * Save the state of the <tt>ConcurrentHashMap</tt> instance to a - * stream (i.e., serialize it). + * Saves the state of the <tt>ConcurrentHashMap</tt> instance to a + * stream (i.e., serializes it). * @param s the stream * @serialData * the key (Object) and value (Object) * for each key-value mapping, followed by a null pair. * The key-value mappings are emitted in no particular order. */ - private void writeObject(java.io.ObjectOutputStream s) throws IOException { + private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException { + // force all segments for serialization compatibility + for (int k = 0; k < segments.length; ++k) + ensureSegment(k); s.defaultWriteObject(); + final Segment<K,V>[] segments = this.segments; for (int k = 0; k < segments.length; ++k) { - Segment<K,V> seg = segments[k]; + Segment<K,V> seg = segmentAt(segments, k); seg.lock(); try { HashEntry<K,V>[] tab = seg.table; for (int i = 0; i < tab.length; ++i) { - for (HashEntry<K,V> e = tab[i]; e != null; e = e.next) { + HashEntry<K,V> e; + for (e = entryAt(tab, i); e != null; e = e.next) { s.writeObject(e.key); s.writeObject(e.value); } @@ -1264,17 +1423,24 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> } /** - * Reconstitute the <tt>ConcurrentHashMap</tt> instance from a - * stream (i.e., deserialize it). + * Reconstitutes the <tt>ConcurrentHashMap</tt> instance from a + * stream (i.e., deserializes it). * @param s the stream */ + @SuppressWarnings("unchecked") private void readObject(java.io.ObjectInputStream s) - throws IOException, ClassNotFoundException { + throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); - // Initialize each segment to be minimally sized, and let grow. - for (int i = 0; i < segments.length; ++i) { - segments[i].setTable(new HashEntry[1]); + // Re-initialize segments to be minimally sized, and let grow. + int cap = MIN_SEGMENT_TABLE_CAPACITY; + final Segment<K,V>[] segments = this.segments; + for (int k = 0; k < segments.length; ++k) { + Segment<K,V> seg = segments[k]; + if (seg != null) { + seg.threshold = (int)(cap * seg.loadFactor); + seg.table = (HashEntry<K,V>[]) new HashEntry<?,?>[cap]; + } } // Read the keys and values, and put the mappings in the table @@ -1286,4 +1452,31 @@ public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> put(key, value); } } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE; + private static final long SBASE; + private static final int SSHIFT; + private static final long TBASE; + private static final int TSHIFT; + + static { + int ss, ts; + try { + UNSAFE = sun.misc.Unsafe.getUnsafe(); + Class<?> tc = HashEntry[].class; + Class<?> sc = Segment[].class; + TBASE = UNSAFE.arrayBaseOffset(tc); + SBASE = UNSAFE.arrayBaseOffset(sc); + ts = UNSAFE.arrayIndexScale(tc); + ss = UNSAFE.arrayIndexScale(sc); + } catch (Exception e) { + throw new Error(e); + } + if ((ss & (ss-1)) != 0 || (ts & (ts-1)) != 0) + throw new Error("data type scale not a power of two"); + SSHIFT = 31 - Integer.numberOfLeadingZeros(ss); + TSHIFT = 31 - Integer.numberOfLeadingZeros(ts); + } + } diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java b/luni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java index 72c2e08..30adb36 100644 --- a/luni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java +++ b/luni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea and Martin Buchholz with assistance from members of * JCP JSR-166 Expert Group and released to the public domain, as explained - * at http://creativecommons.org/licenses/publicdomain + * at http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; @@ -34,10 +34,17 @@ import java.util.Queue; * ConcurrentModificationException}, and may proceed concurrently with * other operations. * - * <p>Beware that, unlike in most collections, the {@code size} - * method is <em>NOT</em> a constant-time operation. Because of the + * <p>Beware that, unlike in most collections, the {@code size} method + * is <em>NOT</em> a constant-time operation. Because of the * asynchronous nature of these deques, determining the current number - * of elements requires a traversal of the elements. + * of elements requires a traversal of the elements, and so may report + * inaccurate results if this collection is modified during traversal. + * Additionally, the bulk operations {@code addAll}, + * {@code removeAll}, {@code retainAll}, {@code containsAll}, + * {@code equals}, and {@code toArray} are <em>not</em> guaranteed + * to be performed atomically. For example, an iterator operating + * concurrently with an {@code addAll} operation might view only some + * of the added elements. * * <p>This class and its iterator implement all of the <em>optional</em> * methods of the {@link Deque} and {@link Iterator} interfaces. @@ -245,13 +252,6 @@ public class ConcurrentLinkedDeque<E> private static final Node<Object> PREV_TERMINATOR, NEXT_TERMINATOR; - static { - PREV_TERMINATOR = new Node<Object>(null); - PREV_TERMINATOR.next = PREV_TERMINATOR; - NEXT_TERMINATOR = new Node<Object>(null); - NEXT_TERMINATOR.prev = NEXT_TERMINATOR; - } - @SuppressWarnings("unchecked") Node<E> prevTerminator() { return (Node<E>) PREV_TERMINATOR; @@ -267,6 +267,9 @@ public class ConcurrentLinkedDeque<E> volatile E item; volatile Node<E> next; + Node() { // default constructor for NEXT_TERMINATOR, PREV_TERMINATOR + } + /** * Constructs a new node. Uses relaxed write because item can * only be seen after publication via casNext or casPrev. @@ -297,14 +300,25 @@ public class ConcurrentLinkedDeque<E> // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE = - sun.misc.Unsafe.getUnsafe(); - private static final long prevOffset = - objectFieldOffset(UNSAFE, "prev", Node.class); - private static final long itemOffset = - objectFieldOffset(UNSAFE, "item", Node.class); - private static final long nextOffset = - objectFieldOffset(UNSAFE, "next", Node.class); + private static final sun.misc.Unsafe UNSAFE; + private static final long prevOffset; + private static final long itemOffset; + private static final long nextOffset; + + static { + try { + UNSAFE = sun.misc.Unsafe.getUnsafe(); + Class<?> k = Node.class; + prevOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("prev")); + itemOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("item")); + nextOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("next")); + } catch (Exception e) { + throw new Error(e); + } + } } /** @@ -1209,8 +1223,7 @@ public class ConcurrentLinkedDeque<E> * The following code can be used to dump the deque into a newly * allocated array of {@code String}: * - * <pre> - * String[] y = x.toArray(new String[0]);</pre> + * <pre> {@code String[] y = x.toArray(new String[0]);}</pre> * * Note that {@code toArray(new Object[0])} is identical in function to * {@code toArray()}. @@ -1395,14 +1408,6 @@ public class ConcurrentLinkedDeque<E> initHeadTail(h, t); } - // Unsafe mechanics - - private static final sun.misc.Unsafe UNSAFE = - sun.misc.Unsafe.getUnsafe(); - private static final long headOffset = - objectFieldOffset(UNSAFE, "head", ConcurrentLinkedDeque.class); - private static final long tailOffset = - objectFieldOffset(UNSAFE, "tail", ConcurrentLinkedDeque.class); private boolean casHead(Node<E> cmp, Node<E> val) { return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val); @@ -1412,15 +1417,25 @@ public class ConcurrentLinkedDeque<E> return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val); } - static long objectFieldOffset(sun.misc.Unsafe UNSAFE, - String field, Class<?> klazz) { + // Unsafe mechanics + + private static final sun.misc.Unsafe UNSAFE; + private static final long headOffset; + private static final long tailOffset; + static { + PREV_TERMINATOR = new Node<Object>(); + PREV_TERMINATOR.next = PREV_TERMINATOR; + NEXT_TERMINATOR = new Node<Object>(); + NEXT_TERMINATOR.prev = NEXT_TERMINATOR; try { - return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field)); - } catch (NoSuchFieldException e) { - // Convert Exception to corresponding Error - NoSuchFieldError error = new NoSuchFieldError(field); - error.initCause(e); - throw error; + UNSAFE = sun.misc.Unsafe.getUnsafe(); + Class<?> k = ConcurrentLinkedDeque.class; + headOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("head")); + tailOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("tail")); + } catch (Exception e) { + throw new Error(e); } } } diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java b/luni/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java index 3ed0a7c..a0a26fd 100644 --- a/luni/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java +++ b/luni/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea and Martin Buchholz with assistance from members of * JCP JSR-166 Expert Group and released to the public domain, as explained - * at http://creativecommons.org/licenses/publicdomain + * at http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; @@ -47,7 +47,14 @@ import java.util.Queue; * <p>Beware that, unlike in most collections, the {@code size} method * is <em>NOT</em> a constant-time operation. Because of the * asynchronous nature of these queues, determining the current number - * of elements requires a traversal of the elements. + * of elements requires a traversal of the elements, and so may report + * inaccurate results if this collection is modified during traversal. + * Additionally, the bulk operations {@code addAll}, + * {@code removeAll}, {@code retainAll}, {@code containsAll}, + * {@code equals}, and {@code toArray} are <em>not</em> guaranteed + * to be performed atomically. For example, an iterator operating + * concurrently with an {@code addAll} operation might view only some + * of the added elements. * * <p>This class and its iterator implement all of the <em>optional</em> * methods of the {@link Queue} and {@link Iterator} interfaces. @@ -165,12 +172,22 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE = - sun.misc.Unsafe.getUnsafe(); - private static final long nextOffset = - objectFieldOffset(UNSAFE, "next", Node.class); - private static final long itemOffset = - objectFieldOffset(UNSAFE, "item", Node.class); + private static final sun.misc.Unsafe UNSAFE; + private static final long itemOffset; + private static final long nextOffset; + + static { + try { + UNSAFE = sun.misc.Unsafe.getUnsafe(); + Class<?> k = Node.class; + itemOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("item")); + nextOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("next")); + } catch (Exception e) { + throw new Error(e); + } + } } /** @@ -563,8 +580,7 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> * The following code can be used to dump the queue into a newly * allocated array of {@code String}: * - * <pre> - * String[] y = x.toArray(new String[0]);</pre> + * <pre> {@code String[] y = x.toArray(new String[0]);}</pre> * * Note that {@code toArray(new Object[0])} is identical in function to * {@code toArray()}. @@ -761,14 +777,6 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> throw new NullPointerException(); } - // Unsafe mechanics - - private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); - private static final long headOffset = - objectFieldOffset(UNSAFE, "head", ConcurrentLinkedQueue.class); - private static final long tailOffset = - objectFieldOffset(UNSAFE, "tail", ConcurrentLinkedQueue.class); - private boolean casTail(Node<E> cmp, Node<E> val) { return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val); } @@ -777,15 +785,21 @@ public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val); } - static long objectFieldOffset(sun.misc.Unsafe UNSAFE, - String field, Class<?> klazz) { + // Unsafe mechanics + + private static final sun.misc.Unsafe UNSAFE; + private static final long headOffset; + private static final long tailOffset; + static { try { - return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field)); - } catch (NoSuchFieldException e) { - // Convert Exception to corresponding Error - NoSuchFieldError error = new NoSuchFieldError(field); - error.initCause(e); - throw error; + UNSAFE = sun.misc.Unsafe.getUnsafe(); + Class<?> k = ConcurrentLinkedQueue.class; + headOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("head")); + tailOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("tail")); + } catch (Exception e) { + throw new Error(e); } } } diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentMap.java b/luni/src/main/java/java/util/concurrent/ConcurrentMap.java index 2daebc5..3405acf 100644 --- a/luni/src/main/java/java/util/concurrent/ConcurrentMap.java +++ b/luni/src/main/java/java/util/concurrent/ConcurrentMap.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; @@ -32,11 +32,12 @@ public interface ConcurrentMap<K, V> extends Map<K, V> { * If the specified key is not already associated * with a value, associate it with the given value. * This is equivalent to - * <pre> - * if (!map.containsKey(key)) - * return map.put(key, value); - * else - * return map.get(key);</pre> + * <pre> {@code + * if (!map.containsKey(key)) + * return map.put(key, value); + * else + * return map.get(key);}</pre> + * * except that the action is performed atomically. * * @param key key with which the specified value is to be associated @@ -61,11 +62,13 @@ public interface ConcurrentMap<K, V> extends Map<K, V> { /** * Removes the entry for a key only if currently mapped to a given value. * This is equivalent to - * <pre> - * if (map.containsKey(key) && map.get(key).equals(value)) { - * map.remove(key); - * return true; - * } else return false;</pre> + * <pre> {@code + * if (map.containsKey(key) && map.get(key).equals(value)) { + * map.remove(key); + * return true; + * } else + * return false;}</pre> + * * except that the action is performed atomically. * * @param key key with which the specified value is associated @@ -74,20 +77,24 @@ public interface ConcurrentMap<K, V> extends Map<K, V> { * @throws UnsupportedOperationException if the <tt>remove</tt> operation * is not supported by this map * @throws ClassCastException if the key or value is of an inappropriate - * type for this map (optional) + * type for this map + * (<a href="../Collection.html#optional-restrictions">optional</a>) * @throws NullPointerException if the specified key or value is null, - * and this map does not permit null keys or values (optional) + * and this map does not permit null keys or values + * (<a href="../Collection.html#optional-restrictions">optional</a>) */ boolean remove(Object key, Object value); /** * Replaces the entry for a key only if currently mapped to a given value. * This is equivalent to - * <pre> - * if (map.containsKey(key) && map.get(key).equals(oldValue)) { - * map.put(key, newValue); - * return true; - * } else return false;</pre> + * <pre> {@code + * if (map.containsKey(key) && map.get(key).equals(oldValue)) { + * map.put(key, newValue); + * return true; + * } else + * return false;}</pre> + * * except that the action is performed atomically. * * @param key key with which the specified value is associated @@ -108,10 +115,12 @@ public interface ConcurrentMap<K, V> extends Map<K, V> { /** * Replaces the entry for a key only if currently mapped to some value. * This is equivalent to - * <pre> - * if (map.containsKey(key)) { - * return map.put(key, value); - * } else return null;</pre> + * <pre> {@code + * if (map.containsKey(key)) { + * return map.put(key, value); + * } else + * return null;}</pre> + * * except that the action is performed atomically. * * @param key key with which the specified value is associated diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentNavigableMap.java b/luni/src/main/java/java/util/concurrent/ConcurrentNavigableMap.java index 7d86afb..ea99886 100644 --- a/luni/src/main/java/java/util/concurrent/ConcurrentNavigableMap.java +++ b/luni/src/main/java/java/util/concurrent/ConcurrentNavigableMap.java @@ -1,20 +1,20 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; import java.util.*; +// BEGIN android-note +// removed link to collections framework docs +// END android-note + /** * A {@link ConcurrentMap} supporting {@link NavigableMap} operations, * and recursively so for its navigable sub-maps. * - * <p>This interface is a member of the - * <a href="{@docRoot}/../technotes/guides/collections/index.html"> - * Java Collections Framework</a>. - * * @author Doug Lea * @param <K> the type of keys maintained by this map * @param <V> the type of mapped values diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java b/luni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java index fbd1083..d0d6f14 100644 --- a/luni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java +++ b/luni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java @@ -1,12 +1,15 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; import java.util.*; -import java.util.concurrent.atomic.*; + +// BEGIN android-note +// removed link to collections framework docs +// END android-note /** * A scalable concurrent {@link ConcurrentNavigableMap} implementation. @@ -15,8 +18,8 @@ import java.util.concurrent.atomic.*; * creation time, depending on which constructor is used. * * <p>This class implements a concurrent variant of <a - * href="http://www.cs.umd.edu/~pugh/">SkipLists</a> providing - * expected average <i>log(n)</i> time cost for the + * href="http://en.wikipedia.org/wiki/Skip_list" target="_top">SkipLists</a> + * providing expected average <i>log(n)</i> time cost for the * <tt>containsKey</tt>, <tt>get</tt>, <tt>put</tt> and * <tt>remove</tt> operations and their variants. Insertion, removal, * update, and access operations safely execute concurrently by @@ -37,12 +40,13 @@ import java.util.concurrent.atomic.*; * <p>Beware that, unlike in most collections, the <tt>size</tt> * method is <em>not</em> a constant-time operation. Because of the * asynchronous nature of these maps, determining the current number - * of elements requires a traversal of the elements. Additionally, - * the bulk operations <tt>putAll</tt>, <tt>equals</tt>, and - * <tt>clear</tt> are <em>not</em> guaranteed to be performed - * atomically. For example, an iterator operating concurrently with a - * <tt>putAll</tt> operation might view only some of the added - * elements. + * of elements requires a traversal of the elements, and so may report + * inaccurate results if this collection is modified during traversal. + * Additionally, the bulk operations <tt>putAll</tt>, <tt>equals</tt>, + * <tt>toArray</tt>, <tt>containsValue</tt>, and <tt>clear</tt> are + * <em>not</em> guaranteed to be performed atomically. For example, an + * iterator operating concurrently with a <tt>putAll</tt> operation + * might view only some of the added elements. * * <p>This class and its views and iterators implement all of the * <em>optional</em> methods of the {@link Map} and {@link Iterator} @@ -51,10 +55,6 @@ import java.util.concurrent.atomic.*; * null return values cannot be reliably distinguished from the absence of * elements. * - * <p>This class is a member of the - * <a href="{@docRoot}/../technotes/guides/collections/index.html"> - * Java Collections Framework</a>. - * * @author Doug Lea * @param <K> the type of keys maintained by this map * @param <V> the type of mapped values @@ -322,11 +322,11 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> private transient int randomSeed; /** Lazily initialized key set */ - private transient KeySet keySet; + private transient KeySet<K> keySet; /** Lazily initialized entry set */ - private transient EntrySet entrySet; + private transient EntrySet<K,V> entrySet; /** Lazily initialized values collection */ - private transient Values values; + private transient Values<V> values; /** Lazily initialized descending key set */ private transient ConcurrentNavigableMap<K,V> descendingMap; @@ -478,13 +478,24 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> return new AbstractMap.SimpleImmutableEntry<K,V>(key, v); } - // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); - private static final long valueOffset = - objectFieldOffset(UNSAFE, "value", Node.class); - private static final long nextOffset = - objectFieldOffset(UNSAFE, "next", Node.class); + // UNSAFE mechanics + private static final sun.misc.Unsafe UNSAFE; + private static final long valueOffset; + private static final long nextOffset; + + static { + try { + UNSAFE = sun.misc.Unsafe.getUnsafe(); + Class<?> k = Node.class; + valueOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("value")); + nextOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("next")); + } catch (Exception e) { + throw new Error(e); + } + } } /* ---------------- Indexing -------------- */ @@ -551,10 +562,18 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> } // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); - private static final long rightOffset = - objectFieldOffset(UNSAFE, "right", Index.class); - + private static final sun.misc.Unsafe UNSAFE; + private static final long rightOffset; + static { + try { + UNSAFE = sun.misc.Unsafe.getUnsafe(); + Class<?> k = Index.class; + rightOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("right")); + } catch (Exception e) { + throw new Error(e); + } + } } /* ---------------- Head nodes -------------- */ @@ -884,7 +903,7 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> * direction. */ level = max + 1; - Index<K,V>[] idxs = (Index<K,V>[])new Index[level+1]; + Index<K,V>[] idxs = (Index<K,V>[])new Index<?,?>[level+1]; Index<K,V> idx = null; for (int i = 1; i <= level; ++i) idxs[i] = idx = new Index<K,V>(z, idx, null); @@ -1387,16 +1406,16 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> * @return a shallow copy of this map */ public ConcurrentSkipListMap<K,V> clone() { - ConcurrentSkipListMap<K,V> clone = null; try { - clone = (ConcurrentSkipListMap<K,V>) super.clone(); + @SuppressWarnings("unchecked") + ConcurrentSkipListMap<K,V> clone = + (ConcurrentSkipListMap<K,V>) super.clone(); + clone.initialize(); + clone.buildFromSorted(this); + return clone; } catch (CloneNotSupportedException e) { throw new InternalError(); } - - clone.initialize(); - clone.buildFromSorted(this); - return clone; } /** @@ -1458,7 +1477,7 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> /* ---------------- Serialization -------------- */ /** - * Save the state of this map to a stream. + * Saves the state of this map to a stream (that is, serializes it). * * @serialData The key (Object) and value (Object) for each * key-value mapping represented by the map, followed by @@ -1483,7 +1502,9 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> } /** - * Reconstitute the map from a stream. + * Reconstitutes the map from a stream (that is, deserializes it). + * + * @param s the stream */ private void readObject(final java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { @@ -1613,7 +1634,9 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> /** * Returns <tt>true</tt> if this map maps one or more keys to the * specified value. This operation requires time linear in the - * map size. + * map size. Additionally, it is possible for the map to change + * during execution of this method, in which case the returned + * result may be inaccurate. * * @param value value whose presence in this map is to be tested * @return <tt>true</tt> if a mapping to <tt>value</tt> exists; @@ -1703,14 +1726,14 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> * * @return a navigable set view of the keys in this map */ - public NavigableSet<K> keySet() { - KeySet ks = keySet; - return (ks != null) ? ks : (keySet = new KeySet(this)); + public NavigableSet<K> keySet() { + KeySet<K> ks = keySet; + return (ks != null) ? ks : (keySet = new KeySet<K>(this)); } public NavigableSet<K> navigableKeySet() { - KeySet ks = keySet; - return (ks != null) ? ks : (keySet = new KeySet(this)); + KeySet<K> ks = keySet; + return (ks != null) ? ks : (keySet = new KeySet<K>(this)); } /** @@ -1732,8 +1755,8 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> * reflect any modifications subsequent to construction. */ public Collection<V> values() { - Values vs = values; - return (vs != null) ? vs : (values = new Values(this)); + Values<V> vs = values; + return (vs != null) ? vs : (values = new Values<V>(this)); } /** @@ -1761,8 +1784,8 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> * sorted in ascending key order */ public Set<Map.Entry<K,V>> entrySet() { - EntrySet es = entrySet; - return (es != null) ? es : (entrySet = new EntrySet(this)); + EntrySet<K,V> es = entrySet; + return (es != null) ? es : (entrySet = new EntrySet<K,V>(this)); } public ConcurrentNavigableMap<K,V> descendingMap() { @@ -2253,8 +2276,8 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> static final class KeySet<E> extends AbstractSet<E> implements NavigableSet<E> { - private final ConcurrentNavigableMap<E,Object> m; - KeySet(ConcurrentNavigableMap<E,Object> map) { m = map; } + private final ConcurrentNavigableMap<E,?> m; + KeySet(ConcurrentNavigableMap<E,?> map) { m = map; } public int size() { return m.size(); } public boolean isEmpty() { return m.isEmpty(); } public boolean contains(Object o) { return m.containsKey(o); } @@ -2268,11 +2291,11 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> public E first() { return m.firstKey(); } public E last() { return m.lastKey(); } public E pollFirst() { - Map.Entry<E,Object> e = m.pollFirstEntry(); + Map.Entry<E,?> e = m.pollFirstEntry(); return (e == null) ? null : e.getKey(); } public E pollLast() { - Map.Entry<E,Object> e = m.pollLastEntry(); + Map.Entry<E,?> e = m.pollLastEntry(); return (e == null) ? null : e.getKey(); } public Iterator<E> iterator() { @@ -2323,20 +2346,20 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> return tailSet(fromElement, true); } public NavigableSet<E> descendingSet() { - return new KeySet(m.descendingMap()); + return new KeySet<E>(m.descendingMap()); } } static final class Values<E> extends AbstractCollection<E> { - private final ConcurrentNavigableMap<Object, E> m; - Values(ConcurrentNavigableMap<Object, E> map) { + private final ConcurrentNavigableMap<?, E> m; + Values(ConcurrentNavigableMap<?, E> map) { m = map; } public Iterator<E> iterator() { if (m instanceof ConcurrentSkipListMap) - return ((ConcurrentSkipListMap<Object,E>)m).valueIterator(); + return ((ConcurrentSkipListMap<?,E>)m).valueIterator(); else - return ((SubMap<Object,E>)m).valueIterator(); + return ((SubMap<?,E>)m).valueIterator(); } public boolean isEmpty() { return m.isEmpty(); @@ -2370,14 +2393,14 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> public boolean contains(Object o) { if (!(o instanceof Map.Entry)) return false; - Map.Entry<K1,V1> e = (Map.Entry<K1,V1>)o; + Map.Entry<?,?> e = (Map.Entry<?,?>)o; V1 v = m.get(e.getKey()); return v != null && v.equals(e.getValue()); } public boolean remove(Object o) { if (!(o instanceof Map.Entry)) return false; - Map.Entry<K1,V1> e = (Map.Entry<K1,V1>)o; + Map.Entry<?,?> e = (Map.Entry<?,?>)o; return m.remove(e.getKey(), e.getValue()); } @@ -2517,9 +2540,9 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> if (lo == null) return m.findFirst(); else if (loInclusive) - return m.findNear(lo, m.GT|m.EQ); + return m.findNear(lo, GT|EQ); else - return m.findNear(lo, m.GT); + return m.findNear(lo, GT); } /** @@ -2530,9 +2553,9 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> if (hi == null) return m.findLast(); else if (hiInclusive) - return m.findNear(hi, m.LT|m.EQ); + return m.findNear(hi, LT|EQ); else - return m.findNear(hi, m.LT); + return m.findNear(hi, LT); } /** @@ -2614,15 +2637,15 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> */ private Map.Entry<K,V> getNearEntry(K key, int rel) { if (isDescending) { // adjust relation for direction - if ((rel & m.LT) == 0) - rel |= m.LT; + if ((rel & LT) == 0) + rel |= LT; else - rel &= ~m.LT; + rel &= ~LT; } if (tooLow(key)) - return ((rel & m.LT) != 0) ? null : lowestEntry(); + return ((rel & LT) != 0) ? null : lowestEntry(); if (tooHigh(key)) - return ((rel & m.LT) != 0) ? highestEntry() : null; + return ((rel & LT) != 0) ? highestEntry() : null; for (;;) { Node<K,V> n = m.findNear(key, rel); if (n == null || !inBounds(n.key)) @@ -2637,13 +2660,13 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> // Almost the same as getNearEntry, except for keys private K getNearKey(K key, int rel) { if (isDescending) { // adjust relation for direction - if ((rel & m.LT) == 0) - rel |= m.LT; + if ((rel & LT) == 0) + rel |= LT; else - rel &= ~m.LT; + rel &= ~LT; } if (tooLow(key)) { - if ((rel & m.LT) == 0) { + if ((rel & LT) == 0) { ConcurrentSkipListMap.Node<K,V> n = loNode(); if (isBeforeEnd(n)) return n.key; @@ -2651,7 +2674,7 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> return null; } if (tooHigh(key)) { - if ((rel & m.LT) != 0) { + if ((rel & LT) != 0) { ConcurrentSkipListMap.Node<K,V> n = hiNode(); if (n != null) { K last = n.key; @@ -2683,7 +2706,7 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> public V get(Object key) { if (key == null) throw new NullPointerException(); K k = (K)key; - return ((!inBounds(k)) ? null : m.get(k)); + return (!inBounds(k)) ? null : m.get(k); } public V put(K key, V value) { @@ -2850,35 +2873,35 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> /* ---------------- Relational methods -------------- */ public Map.Entry<K,V> ceilingEntry(K key) { - return getNearEntry(key, (m.GT|m.EQ)); + return getNearEntry(key, GT|EQ); } public K ceilingKey(K key) { - return getNearKey(key, (m.GT|m.EQ)); + return getNearKey(key, GT|EQ); } public Map.Entry<K,V> lowerEntry(K key) { - return getNearEntry(key, (m.LT)); + return getNearEntry(key, LT); } public K lowerKey(K key) { - return getNearKey(key, (m.LT)); + return getNearKey(key, LT); } public Map.Entry<K,V> floorEntry(K key) { - return getNearEntry(key, (m.LT|m.EQ)); + return getNearEntry(key, LT|EQ); } public K floorKey(K key) { - return getNearKey(key, (m.LT|m.EQ)); + return getNearKey(key, LT|EQ); } public Map.Entry<K,V> higherEntry(K key) { - return getNearEntry(key, (m.GT)); + return getNearEntry(key, GT); } public K higherKey(K key) { - return getNearKey(key, (m.GT)); + return getNearKey(key, GT); } public K firstKey() { @@ -2909,22 +2932,22 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> public NavigableSet<K> keySet() { KeySet<K> ks = keySetView; - return (ks != null) ? ks : (keySetView = new KeySet(this)); + return (ks != null) ? ks : (keySetView = new KeySet<K>(this)); } public NavigableSet<K> navigableKeySet() { KeySet<K> ks = keySetView; - return (ks != null) ? ks : (keySetView = new KeySet(this)); + return (ks != null) ? ks : (keySetView = new KeySet<K>(this)); } public Collection<V> values() { Collection<V> vs = valuesView; - return (vs != null) ? vs : (valuesView = new Values(this)); + return (vs != null) ? vs : (valuesView = new Values<V>(this)); } public Set<Map.Entry<K,V>> entrySet() { Set<Map.Entry<K,V>> es = entrySetView; - return (es != null) ? es : (entrySetView = new EntrySet(this)); + return (es != null) ? es : (entrySetView = new EntrySet<K,V>(this)); } public NavigableSet<K> descendingKeySet() { @@ -3053,20 +3076,16 @@ public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> } // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); - private static final long headOffset = - objectFieldOffset(UNSAFE, "head", ConcurrentSkipListMap.class); - - static long objectFieldOffset(sun.misc.Unsafe UNSAFE, - String field, Class<?> klazz) { + private static final sun.misc.Unsafe UNSAFE; + private static final long headOffset; + static { try { - return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field)); - } catch (NoSuchFieldException e) { - // Convert Exception to corresponding Error - NoSuchFieldError error = new NoSuchFieldError(field); - error.initCause(e); - throw error; + UNSAFE = sun.misc.Unsafe.getUnsafe(); + Class<?> k = ConcurrentSkipListMap.class; + headOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("head")); + } catch (Exception e) { + throw new Error(e); } } - } diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentSkipListSet.java b/luni/src/main/java/java/util/concurrent/ConcurrentSkipListSet.java index d24876f..71431a9 100644 --- a/luni/src/main/java/java/util/concurrent/ConcurrentSkipListSet.java +++ b/luni/src/main/java/java/util/concurrent/ConcurrentSkipListSet.java @@ -1,12 +1,15 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; import java.util.*; -import sun.misc.Unsafe; + +// BEGIN android-note +// removed link to collections framework docs +// END android-note /** * A scalable concurrent {@link NavigableSet} implementation based on @@ -29,12 +32,14 @@ import sun.misc.Unsafe; * <p>Beware that, unlike in most collections, the <tt>size</tt> * method is <em>not</em> a constant-time operation. Because of the * asynchronous nature of these sets, determining the current number - * of elements requires a traversal of the elements. Additionally, the - * bulk operations <tt>addAll</tt>, <tt>removeAll</tt>, - * <tt>retainAll</tt>, and <tt>containsAll</tt> are <em>not</em> - * guaranteed to be performed atomically. For example, an iterator - * operating concurrently with an <tt>addAll</tt> operation might view - * only some of the added elements. + * of elements requires a traversal of the elements, and so may report + * inaccurate results if this collection is modified during traversal. + * Additionally, the bulk operations <tt>addAll</tt>, + * <tt>removeAll</tt>, <tt>retainAll</tt>, <tt>containsAll</tt>, + * <tt>equals</tt>, and <tt>toArray</tt> are <em>not</em> guaranteed + * to be performed atomically. For example, an iterator operating + * concurrently with an <tt>addAll</tt> operation might view only some + * of the added elements. * * <p>This class and its iterators implement all of the * <em>optional</em> methods of the {@link Set} and {@link Iterator} @@ -43,10 +48,6 @@ import sun.misc.Unsafe; * because <tt>null</tt> arguments and return values cannot be reliably * distinguished from the absence of elements. * - * <p>This class is a member of the - * <a href="{@docRoot}/../technotes/guides/collections/index.html"> - * Java Collections Framework</a>. - * * @author Doug Lea * @param <E> the type of elements maintained by this set * @since 1.6 @@ -127,15 +128,15 @@ public class ConcurrentSkipListSet<E> * @return a shallow copy of this set */ public ConcurrentSkipListSet<E> clone() { - ConcurrentSkipListSet<E> clone = null; try { - clone = (ConcurrentSkipListSet<E>) super.clone(); - clone.setMap(new ConcurrentSkipListMap(m)); + @SuppressWarnings("unchecked") + ConcurrentSkipListSet<E> clone = + (ConcurrentSkipListSet<E>) super.clone(); + clone.setMap(new ConcurrentSkipListMap<E,Object>(m)); + return clone; } catch (CloneNotSupportedException e) { throw new InternalError(); } - - return clone; } /* ---------------- Set operations -------------- */ @@ -291,8 +292,8 @@ public class ConcurrentSkipListSet<E> public boolean removeAll(Collection<?> c) { // Override AbstractSet version to avoid unnecessary call to size() boolean modified = false; - for (Iterator<?> i = c.iterator(); i.hasNext(); ) - if (remove(i.next())) + for (Object e : c) + if (remove(e)) modified = true; return modified; } @@ -437,20 +438,24 @@ public class ConcurrentSkipListSet<E> * @return a reverse order view of this set */ public NavigableSet<E> descendingSet() { - return new ConcurrentSkipListSet(m.descendingMap()); + return new ConcurrentSkipListSet<E>(m.descendingMap()); } // Support for resetting map in clone - private static final Unsafe unsafe = Unsafe.getUnsafe(); + private void setMap(ConcurrentNavigableMap<E,Object> map) { + UNSAFE.putObjectVolatile(this, mapOffset, map); + } + + private static final sun.misc.Unsafe UNSAFE; private static final long mapOffset; static { try { - mapOffset = unsafe.objectFieldOffset - (ConcurrentSkipListSet.class.getDeclaredField("m")); - } catch (Exception ex) { throw new Error(ex); } - } - private void setMap(ConcurrentNavigableMap<E,Object> map) { - unsafe.putObjectVolatile(this, mapOffset, map); + UNSAFE = sun.misc.Unsafe.getUnsafe(); + Class<?> k = ConcurrentSkipListSet.class; + mapOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("m")); + } catch (Exception e) { + throw new Error(e); + } } - } diff --git a/luni/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java b/luni/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java index 87419fc..1f37bc9 100644 --- a/luni/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java +++ b/luni/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; @@ -160,8 +160,7 @@ public class CopyOnWriteArraySet<E> extends AbstractSet<E> * The following code can be used to dump the set into a newly allocated * array of <tt>String</tt>: * - * <pre> - * String[] y = x.toArray(new String[0]);</pre> + * <pre> {@code String[] y = x.toArray(new String[0]);}</pre> * * Note that <tt>toArray(new Object[0])</tt> is identical in function to * <tt>toArray()</tt>. @@ -358,6 +357,6 @@ public class CopyOnWriteArraySet<E> extends AbstractSet<E> * Test for equality, coping with nulls. */ private static boolean eq(Object o1, Object o2) { - return (o1 == null ? o2 == null : o1.equals(o2)); + return (o1 == null) ? o2 == null : o1.equals(o2); } } diff --git a/luni/src/main/java/java/util/concurrent/CountDownLatch.java b/luni/src/main/java/java/util/concurrent/CountDownLatch.java index 1888279..e90badf 100644 --- a/luni/src/main/java/java/util/concurrent/CountDownLatch.java +++ b/luni/src/main/java/java/util/concurrent/CountDownLatch.java @@ -1,12 +1,11 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; import java.util.concurrent.locks.*; -import java.util.concurrent.atomic.*; /** * A synchronization aid that allows one or more threads to wait until @@ -44,7 +43,7 @@ import java.util.concurrent.atomic.*; * until all workers have completed. * </ul> * - * <pre> + * <pre> {@code * class Driver { // ... * void main() throws InterruptedException { * CountDownLatch startSignal = new CountDownLatch(1); @@ -76,9 +75,7 @@ import java.util.concurrent.atomic.*; * } * * void doWork() { ... } - * } - * - * </pre> + * }}</pre> * * <p>Another typical usage would be to divide a problem into N parts, * describe each part with a Runnable that executes that portion and @@ -87,7 +84,7 @@ import java.util.concurrent.atomic.*; * will be able to pass through await. (When threads must repeatedly * count down in this way, instead use a {@link CyclicBarrier}.) * - * <pre> + * <pre> {@code * class Driver2 { // ... * void main() throws InterruptedException { * CountDownLatch doneSignal = new CountDownLatch(N); @@ -115,9 +112,7 @@ import java.util.concurrent.atomic.*; * } * * void doWork() { ... } - * } - * - * </pre> + * }}</pre> * * <p>Memory consistency effects: Until the count reaches * zero, actions in a thread prior to calling diff --git a/luni/src/main/java/java/util/concurrent/CyclicBarrier.java b/luni/src/main/java/java/util/concurrent/CyclicBarrier.java index d5738c5..cf0b46e 100644 --- a/luni/src/main/java/java/util/concurrent/CyclicBarrier.java +++ b/luni/src/main/java/java/util/concurrent/CyclicBarrier.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; @@ -23,7 +23,8 @@ import java.util.concurrent.locks.*; * * <p><b>Sample usage:</b> Here is an example of * using a barrier in a parallel decomposition design: - * <pre> + * + * <pre> {@code * class Solver { * final int N; * final float[][] data; @@ -61,8 +62,8 @@ import java.util.concurrent.locks.*; * * waitUntilDone(); * } - * } - * </pre> + * }}</pre> + * * Here, each worker thread processes a row of the matrix then waits at the * barrier until all rows have been processed. When all rows are processed * the supplied {@link Runnable} barrier action is executed and merges the @@ -76,9 +77,10 @@ import java.util.concurrent.locks.*; * {@link #await} returns the arrival index of that thread at the barrier. * You can then choose which thread should execute the barrier action, for * example: - * <pre> if (barrier.await() == 0) { - * // log the completion of this iteration - * }</pre> + * <pre> {@code + * if (barrier.await() == 0) { + * // log the completion of this iteration + * }}</pre> * * <p>The <tt>CyclicBarrier</tt> uses an all-or-none breakage model * for failed synchronization attempts: If a thread leaves a barrier @@ -175,21 +177,21 @@ public class CyclicBarrier { throw new InterruptedException(); } - int index = --count; - if (index == 0) { // tripped - boolean ranAction = false; - try { - final Runnable command = barrierCommand; - if (command != null) - command.run(); - ranAction = true; - nextGeneration(); - return 0; - } finally { - if (!ranAction) - breakBarrier(); - } - } + int index = --count; + if (index == 0) { // tripped + boolean ranAction = false; + try { + final Runnable command = barrierCommand; + if (command != null) + command.run(); + ranAction = true; + nextGeneration(); + return 0; + } finally { + if (!ranAction) + breakBarrier(); + } + } // loop until tripped, broken, interrupted, or timed out for (;;) { @@ -325,7 +327,7 @@ public class CyclicBarrier { try { return dowait(false, 0L); } catch (TimeoutException toe) { - throw new Error(toe); // cannot happen; + throw new Error(toe); // cannot happen } } diff --git a/luni/src/main/java/java/util/concurrent/DelayQueue.java b/luni/src/main/java/java/util/concurrent/DelayQueue.java index 8c44e82..52028cb 100644 --- a/luni/src/main/java/java/util/concurrent/DelayQueue.java +++ b/luni/src/main/java/java/util/concurrent/DelayQueue.java @@ -1,12 +1,14 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; -import java.util.concurrent.locks.*; +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; import java.util.*; // BEGIN android-note @@ -29,7 +31,9 @@ import java.util.*; * * <p>This class and its iterator implement all of the * <em>optional</em> methods of the {@link Collection} and {@link - * Iterator} interfaces. + * Iterator} interfaces. The Iterator provided in method {@link + * #iterator()} is <em>not</em> guaranteed to traverse the elements of + * the DelayQueue in any particular order. * * @since 1.5 * @author Doug Lea @@ -154,7 +158,7 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> lock.lock(); try { E first = q.peek(); - if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0) + if (first == null || first.getDelay(NANOSECONDS) > 0) return null; else return q.poll(); @@ -179,7 +183,7 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> if (first == null) available.await(); else { - long delay = first.getDelay(TimeUnit.NANOSECONDS); + long delay = first.getDelay(NANOSECONDS); if (delay <= 0) return q.poll(); else if (leader != null) @@ -226,7 +230,7 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> else nanos = available.awaitNanos(nanos); } else { - long delay = first.getDelay(TimeUnit.NANOSECONDS); + long delay = first.getDelay(NANOSECONDS); if (delay <= 0) return q.poll(); if (nanos <= 0) @@ -284,6 +288,17 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> } /** + * Return first element only if it is expired. + * Used only by drainTo. Call only when holding lock. + */ + private E peekExpired() { + // assert lock.isHeldByCurrentThread(); + E first = q.peek(); + return (first == null || first.getDelay(NANOSECONDS) > 0) ? + null : first; + } + + /** * @throws UnsupportedOperationException {@inheritDoc} * @throws ClassCastException {@inheritDoc} * @throws NullPointerException {@inheritDoc} @@ -298,11 +313,9 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> lock.lock(); try { int n = 0; - for (;;) { - E first = q.peek(); - if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0) - break; - c.add(q.poll()); + for (E e; (e = peekExpired()) != null;) { + c.add(e); // In this order, in case add() throws. + q.poll(); ++n; } return n; @@ -328,11 +341,9 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> lock.lock(); try { int n = 0; - while (n < maxElements) { - E first = q.peek(); - if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0) - break; - c.add(q.poll()); + for (E e; n < maxElements && (e = peekExpired()) != null;) { + c.add(e); // In this order, in case add() throws. + q.poll(); ++n; } return n; @@ -411,8 +422,7 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> * <p>The following code can be used to dump a delay queue into a newly * allocated array of <tt>Delayed</tt>: * - * <pre> - * Delayed[] a = q.toArray(new Delayed[0]);</pre> + * <pre> {@code Delayed[] a = q.toArray(new Delayed[0]);}</pre> * * Note that <tt>toArray(new Object[0])</tt> is identical in function to * <tt>toArray()</tt>. @@ -451,6 +461,24 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> } /** + * Identity-based version for use in Itr.remove + */ + void removeEQ(Object o) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + for (Iterator<E> it = q.iterator(); it.hasNext(); ) { + if (o == it.next()) { + it.remove(); + break; + } + } + } finally { + lock.unlock(); + } + } + + /** * Returns an iterator over all the elements (both expired and * unexpired) in this queue. The iterator does not return the * elements in any particular order. @@ -473,7 +501,7 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> */ private class Itr implements Iterator<E> { final Object[] array; // Array of all elements - int cursor; // index of next element to return; + int cursor; // index of next element to return int lastRet; // index of last element, or -1 if no such Itr(Object[] array) { @@ -496,21 +524,8 @@ public class DelayQueue<E extends Delayed> extends AbstractQueue<E> public void remove() { if (lastRet < 0) throw new IllegalStateException(); - Object x = array[lastRet]; + removeEQ(array[lastRet]); lastRet = -1; - // Traverse underlying queue to find == element, - // not just a .equals element. - lock.lock(); - try { - for (Iterator it = q.iterator(); it.hasNext(); ) { - if (it.next() == x) { - it.remove(); - return; - } - } - } finally { - lock.unlock(); - } } } diff --git a/luni/src/main/java/java/util/concurrent/Delayed.java b/luni/src/main/java/java/util/concurrent/Delayed.java index b1ff4ee..39d927c 100644 --- a/luni/src/main/java/java/util/concurrent/Delayed.java +++ b/luni/src/main/java/java/util/concurrent/Delayed.java @@ -1,13 +1,11 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; -import java.util.*; - /** * A mix-in style interface for marking objects that should be * acted upon after a given delay. diff --git a/luni/src/main/java/java/util/concurrent/Exchanger.java b/luni/src/main/java/java/util/concurrent/Exchanger.java index 3c230be..6069dce 100644 --- a/luni/src/main/java/java/util/concurrent/Exchanger.java +++ b/luni/src/main/java/java/util/concurrent/Exchanger.java @@ -2,7 +2,7 @@ * Written by Doug Lea, Bill Scherer, and Michael Scott with * assistance from members of JCP JSR-166 Expert Group and released to * the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; @@ -23,7 +23,7 @@ import java.util.concurrent.locks.LockSupport; * to swap buffers between threads so that the thread filling the * buffer gets a freshly emptied one when it needs it, handing off the * filled one to the thread emptying the buffer. - * <pre>{@code + * <pre> {@code * class FillAndEmpty { * Exchanger<DataBuffer> exchanger = new Exchanger<DataBuffer>(); * DataBuffer initialEmptyBuffer = ... a made-up type @@ -59,8 +59,7 @@ import java.util.concurrent.locks.LockSupport; * new Thread(new FillingLoop()).start(); * new Thread(new EmptyingLoop()).start(); * } - * } - * }</pre> + * }}</pre> * * <p>Memory consistency effects: For each pair of threads that * successfully exchange objects via an {@code Exchanger}, actions @@ -135,8 +134,8 @@ public class Exchanger<V> { * races between two threads or thread pre-emptions occurring * between reading and CASing. Also, very transient peak * contention can be much higher than the average sustainable - * levels. The max limit is decreased on average 50% of the times - * that a non-slot-zero wait elapses without being fulfilled. + * levels. An attempt to decrease the max limit is usually made + * when a non-slot-zero wait elapses without being fulfilled. * Threads experiencing elapsed waits move closer to zero, so * eventually find existing (or future) threads even if the table * has been shrunk due to inactivity. The chosen mechanics and @@ -275,7 +274,9 @@ public class Exchanger<V> { * extra space. */ private static final class Slot extends AtomicReference<Object> { - // Improve likelihood of isolation on <= 64 byte cache lines + // Improve likelihood of isolation on <= 128 byte cache lines. + // We used to target 64 byte cache lines, but some x86s (including + // i7 under some BIOSes) actually use 128 byte cache lines. long q0, q1, q2, q3, q4, q5, q6, q7, q8, q9, qa, qb, qc, qd, qe; } diff --git a/luni/src/main/java/java/util/concurrent/ExecutionException.java b/luni/src/main/java/java/util/concurrent/ExecutionException.java index bc561e5..9bb8dee 100644 --- a/luni/src/main/java/java/util/concurrent/ExecutionException.java +++ b/luni/src/main/java/java/util/concurrent/ExecutionException.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; @@ -50,11 +50,9 @@ public class ExecutionException extends Exception { /** * Constructs an <tt>ExecutionException</tt> with the specified cause. - * The detail message is set to: - * <pre> - * (cause == null ? null : cause.toString())</pre> - * (which typically contains the class and detail message of - * <tt>cause</tt>). + * The detail message is set to {@code (cause == null ? null : + * cause.toString())} (which typically contains the class and + * detail message of <tt>cause</tt>). * * @param cause the cause (which is saved for later retrieval by the * {@link #getCause()} method) diff --git a/luni/src/main/java/java/util/concurrent/Executor.java b/luni/src/main/java/java/util/concurrent/Executor.java index fbc4e6f..831bf46 100644 --- a/luni/src/main/java/java/util/concurrent/Executor.java +++ b/luni/src/main/java/java/util/concurrent/Executor.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; @@ -27,23 +27,23 @@ package java.util.concurrent; * executor can run the submitted task immediately in the caller's * thread: * - * <pre> + * <pre> {@code * class DirectExecutor implements Executor { - * public void execute(Runnable r) { - * r.run(); - * } - * }</pre> + * public void execute(Runnable r) { + * r.run(); + * } + * }}</pre> * * More typically, tasks are executed in some thread other * than the caller's thread. The executor below spawns a new thread * for each task. * - * <pre> + * <pre> {@code * class ThreadPerTaskExecutor implements Executor { - * public void execute(Runnable r) { - * new Thread(r).start(); - * } - * }</pre> + * public void execute(Runnable r) { + * new Thread(r).start(); + * } + * }}</pre> * * Many <tt>Executor</tt> implementations impose some sort of * limitation on how and when tasks are scheduled. The executor below diff --git a/luni/src/main/java/java/util/concurrent/ExecutorCompletionService.java b/luni/src/main/java/java/util/concurrent/ExecutorCompletionService.java index b41955d..c0d6006 100644 --- a/luni/src/main/java/java/util/concurrent/ExecutorCompletionService.java +++ b/luni/src/main/java/java/util/concurrent/ExecutorCompletionService.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; diff --git a/luni/src/main/java/java/util/concurrent/ExecutorService.java b/luni/src/main/java/java/util/concurrent/ExecutorService.java index 89688e4..a33ceec 100644 --- a/luni/src/main/java/java/util/concurrent/ExecutorService.java +++ b/luni/src/main/java/java/util/concurrent/ExecutorService.java @@ -1,14 +1,16 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; import java.util.List; import java.util.Collection; -import java.security.PrivilegedAction; -import java.security.PrivilegedExceptionAction; + +// BEGIN android-note +// removed security manager docs +// END android-note /** * An {@link Executor} that provides methods to manage termination and @@ -44,7 +46,7 @@ import java.security.PrivilegedExceptionAction; * pool service incoming requests. It uses the preconfigured {@link * Executors#newFixedThreadPool} factory method: * - * <pre> + * <pre> {@code * class NetworkService implements Runnable { * private final ServerSocket serverSocket; * private final ExecutorService pool; @@ -72,14 +74,13 @@ import java.security.PrivilegedExceptionAction; * public void run() { * // read and service request on socket * } - * } - * </pre> + * }}</pre> * * The following method shuts down an <tt>ExecutorService</tt> in two phases, * first by calling <tt>shutdown</tt> to reject incoming tasks, and then * calling <tt>shutdownNow</tt>, if necessary, to cancel any lingering tasks: * - * <pre> + * <pre> {@code * void shutdownAndAwaitTermination(ExecutorService pool) { * pool.shutdown(); // Disable new tasks from being submitted * try { @@ -96,8 +97,7 @@ import java.security.PrivilegedExceptionAction; * // Preserve interrupt status * Thread.currentThread().interrupt(); * } - * } - * </pre> + * }}</pre> * * <p>Memory consistency effects: Actions in a thread prior to the * submission of a {@code Runnable} or {@code Callable} task to an diff --git a/luni/src/main/java/java/util/concurrent/Executors.java b/luni/src/main/java/java/util/concurrent/Executors.java index e42b0cc..b4f03ba 100644 --- a/luni/src/main/java/java/util/concurrent/Executors.java +++ b/luni/src/main/java/java/util/concurrent/Executors.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; @@ -12,9 +12,10 @@ import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedExceptionAction; import java.security.PrivilegedActionException; -import java.security.AccessControlException; -// import sun.security.util.SecurityConstants; // android-removed +// BEGIN android-note +// removed security manager docs +// END android-note /** * Factory and utility methods for {@link Executor}, {@link * ExecutorService}, {@link ScheduledExecutorService}, {@link @@ -271,10 +272,7 @@ public class Executors { /** * Returns a default thread factory used to create new threads. * This factory creates all new threads used by an Executor in the - * same {@link ThreadGroup}. If there is a {@link - * java.lang.SecurityManager}, it uses the group of {@link - * System#getSecurityManager}, else the group of the thread - * invoking this <tt>defaultThreadFactory</tt> method. Each new + * same {@link ThreadGroup}. Each new * thread is created as a non-daemon thread with priority set to * the smaller of <tt>Thread.NORM_PRIORITY</tt> and the maximum * priority permitted in the thread group. New threads have names @@ -289,36 +287,7 @@ public class Executors { } /** - * Returns a thread factory used to create new threads that - * have the same permissions as the current thread. - * This factory creates threads with the same settings as {@link - * Executors#defaultThreadFactory}, additionally setting the - * AccessControlContext and contextClassLoader of new threads to - * be the same as the thread invoking this - * <tt>privilegedThreadFactory</tt> method. A new - * <tt>privilegedThreadFactory</tt> can be created within an - * {@link AccessController#doPrivileged} action setting the - * current thread's access control context to create threads with - * the selected permission settings holding within that action. - * - * <p> Note that while tasks running within such threads will have - * the same access control and class loader settings as the - * current thread, they need not have the same {@link - * java.lang.ThreadLocal} or {@link - * java.lang.InheritableThreadLocal} values. If necessary, - * particular values of thread locals can be set or reset before - * any task runs in {@link ThreadPoolExecutor} subclasses using - * {@link ThreadPoolExecutor#beforeExecute}. Also, if it is - * necessary to initialize worker threads to have the same - * InheritableThreadLocal settings as some other designated - * thread, you can create a custom ThreadFactory in which that - * thread waits for and services requests to create others that - * will inherit its values. - * - * @return a thread factory - * @throws AccessControlException if the current access control - * context does not have permission to both get and set context - * class loader. + * Legacy security code; do not use. */ public static ThreadFactory privilegedThreadFactory() { return new PrivilegedThreadFactory(); @@ -383,18 +352,7 @@ public class Executors { } /** - * Returns a {@link Callable} object that will, when - * called, execute the given <tt>callable</tt> under the current - * access control context. This method should normally be - * invoked within an {@link AccessController#doPrivileged} action - * to create callables that will, if possible, execute under the - * selected permission settings holding within that action; or if - * not possible, throw an associated {@link - * AccessControlException}. - * @param callable the underlying task - * @return a callable object - * @throws NullPointerException if callable null - * + * Legacy security code; do not use. */ public static <T> Callable<T> privilegedCallable(Callable<T> callable) { if (callable == null) @@ -405,20 +363,10 @@ public class Executors { /** * Returns a {@link Callable} object that will, when * called, execute the given <tt>callable</tt> under the current - * access control context, with the current context class loader - * as the context class loader. This method should normally be - * invoked within an {@link AccessController#doPrivileged} action - * to create callables that will, if possible, execute under the - * selected permission settings holding within that action; or if - * not possible, throw an associated {@link - * AccessControlException}. - * @param callable the underlying task + * with the current context class loader as the context class loader. * * @return a callable object * @throws NullPointerException if callable null - * @throws AccessControlException if the current access control - * context does not have permission to both set and get context - * class loader. */ public static <T> Callable<T> privilegedCallableUsingCurrentClassLoader(Callable<T> callable) { if (callable == null) @@ -480,17 +428,19 @@ public class Executors { private final ClassLoader ccl; PrivilegedCallableUsingCurrentClassLoader(Callable<T> task) { - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - // Calls to getContextClassLoader from this class - // never trigger a security check, but we check - // whether our callers have this permission anyways. - sm.checkPermission(new RuntimePermission("getContextClassLoader")); // android-changed - - // Whether setContextClassLoader turns out to be necessary - // or not, we fail fast if permission is not available. - sm.checkPermission(new RuntimePermission("setContextClassLoader")); - } + // BEGIN android-removed + // SecurityManager sm = System.getSecurityManager(); + // if (sm != null) { + // // Calls to getContextClassLoader from this class + // // never trigger a security check, but we check + // // whether our callers have this permission anyways. + // sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION); + // + // // Whether setContextClassLoader turns out to be necessary + // // or not, we fail fast if permission is not available. + // sm.checkPermission(new RuntimePermission("setContextClassLoader")); + // } + // END android-removed this.task = task; this.acc = AccessController.getContext(); this.ccl = Thread.currentThread().getContextClassLoader(); @@ -561,16 +511,18 @@ public class Executors { PrivilegedThreadFactory() { super(); - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - // Calls to getContextClassLoader from this class - // never trigger a security check, but we check - // whether our callers have this permission anyways. - sm.checkPermission(new RuntimePermission("getContextClassLoader")); // android-changed - - // Fail fast - sm.checkPermission(new RuntimePermission("setContextClassLoader")); - } + // BEGIN android-removed + // SecurityManager sm = System.getSecurityManager(); + // if (sm != null) { + // // Calls to getContextClassLoader from this class + // // never trigger a security check, but we check + // // whether our callers have this permission anyways. + // sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION); + // + // // Fail fast + // sm.checkPermission(new RuntimePermission("setContextClassLoader")); + // } + // END android-removed this.acc = AccessController.getContext(); this.ccl = Thread.currentThread().getContextClassLoader(); } diff --git a/luni/src/main/java/java/util/concurrent/ForkJoinPool.java b/luni/src/main/java/java/util/concurrent/ForkJoinPool.java new file mode 100644 index 0000000..ee15ac8 --- /dev/null +++ b/luni/src/main/java/java/util/concurrent/ForkJoinPool.java @@ -0,0 +1,2127 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +package java.util.concurrent; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.concurrent.AbstractExecutorService; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.RunnableFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.LockSupport; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.Condition; +import libcore.util.SneakyThrow; + +// BEGIN android-note +// removed security manager docs +// END android-note + +/** + * An {@link ExecutorService} for running {@link ForkJoinTask}s. + * A {@code ForkJoinPool} provides the entry point for submissions + * from non-{@code ForkJoinTask} clients, as well as management and + * monitoring operations. + * + * <p>A {@code ForkJoinPool} differs from other kinds of {@link + * ExecutorService} mainly by virtue of employing + * <em>work-stealing</em>: all threads in the pool attempt to find and + * execute subtasks created by other active tasks (eventually blocking + * waiting for work if none exist). This enables efficient processing + * when most tasks spawn other subtasks (as do most {@code + * ForkJoinTask}s). When setting <em>asyncMode</em> to true in + * constructors, {@code ForkJoinPool}s may also be appropriate for use + * with event-style tasks that are never joined. + * + * <p>A {@code ForkJoinPool} is constructed with a given target + * parallelism level; by default, equal to the number of available + * processors. The pool attempts to maintain enough active (or + * available) threads by dynamically adding, suspending, or resuming + * internal worker threads, even if some tasks are stalled waiting to + * join others. However, no such adjustments are guaranteed in the + * face of blocked IO or other unmanaged synchronization. The nested + * {@link ManagedBlocker} interface enables extension of the kinds of + * synchronization accommodated. + * + * <p>In addition to execution and lifecycle control methods, this + * class provides status check methods (for example + * {@link #getStealCount}) that are intended to aid in developing, + * tuning, and monitoring fork/join applications. Also, method + * {@link #toString} returns indications of pool state in a + * convenient form for informal monitoring. + * + * <p> As is the case with other ExecutorServices, there are three + * main task execution methods summarized in the following + * table. These are designed to be used by clients not already engaged + * in fork/join computations in the current pool. The main forms of + * these methods accept instances of {@code ForkJoinTask}, but + * overloaded forms also allow mixed execution of plain {@code + * Runnable}- or {@code Callable}- based activities as well. However, + * tasks that are already executing in a pool should normally + * <em>NOT</em> use these pool execution methods, but instead use the + * within-computation forms listed in the table. + * + * <table BORDER CELLPADDING=3 CELLSPACING=1> + * <tr> + * <td></td> + * <td ALIGN=CENTER> <b>Call from non-fork/join clients</b></td> + * <td ALIGN=CENTER> <b>Call from within fork/join computations</b></td> + * </tr> + * <tr> + * <td> <b>Arrange async execution</td> + * <td> {@link #execute(ForkJoinTask)}</td> + * <td> {@link ForkJoinTask#fork}</td> + * </tr> + * <tr> + * <td> <b>Await and obtain result</td> + * <td> {@link #invoke(ForkJoinTask)}</td> + * <td> {@link ForkJoinTask#invoke}</td> + * </tr> + * <tr> + * <td> <b>Arrange exec and obtain Future</td> + * <td> {@link #submit(ForkJoinTask)}</td> + * <td> {@link ForkJoinTask#fork} (ForkJoinTasks <em>are</em> Futures)</td> + * </tr> + * </table> + * + * <p><b>Sample Usage.</b> Normally a single {@code ForkJoinPool} is + * used for all parallel task execution in a program or subsystem. + * Otherwise, use would not usually outweigh the construction and + * bookkeeping overhead of creating a large set of threads. For + * example, a common pool could be used for the {@code SortTasks} + * illustrated in {@link RecursiveAction}. Because {@code + * ForkJoinPool} uses threads in {@linkplain java.lang.Thread#isDaemon + * daemon} mode, there is typically no need to explicitly {@link + * #shutdown} such a pool upon program exit. + * + * <pre> {@code + * static final ForkJoinPool mainPool = new ForkJoinPool(); + * ... + * public void sort(long[] array) { + * mainPool.invoke(new SortTask(array, 0, array.length)); + * }}</pre> + * + * <p><b>Implementation notes</b>: This implementation restricts the + * maximum number of running threads to 32767. Attempts to create + * pools with greater than the maximum number result in + * {@code IllegalArgumentException}. + * + * <p>This implementation rejects submitted tasks (that is, by throwing + * {@link RejectedExecutionException}) only when the pool is shut down + * or internal resources have been exhausted. + * + * @since 1.7 + * @hide + * @author Doug Lea + */ +public class ForkJoinPool extends AbstractExecutorService { + + /* + * Implementation Overview + * + * This class provides the central bookkeeping and control for a + * set of worker threads: Submissions from non-FJ threads enter + * into a submission queue. Workers take these tasks and typically + * split them into subtasks that may be stolen by other workers. + * Preference rules give first priority to processing tasks from + * their own queues (LIFO or FIFO, depending on mode), then to + * randomized FIFO steals of tasks in other worker queues, and + * lastly to new submissions. + * + * The main throughput advantages of work-stealing stem from + * decentralized control -- workers mostly take tasks from + * themselves or each other. We cannot negate this in the + * implementation of other management responsibilities. The main + * tactic for avoiding bottlenecks is packing nearly all + * essentially atomic control state into a single 64bit volatile + * variable ("ctl"). This variable is read on the order of 10-100 + * times as often as it is modified (always via CAS). (There is + * some additional control state, for example variable "shutdown" + * for which we can cope with uncoordinated updates.) This + * streamlines synchronization and control at the expense of messy + * constructions needed to repack status bits upon updates. + * Updates tend not to contend with each other except during + * bursts while submitted tasks begin or end. In some cases when + * they do contend, threads can instead do something else + * (usually, scan for tasks) until contention subsides. + * + * To enable packing, we restrict maximum parallelism to (1<<15)-1 + * (which is far in excess of normal operating range) to allow + * ids, counts, and their negations (used for thresholding) to fit + * into 16bit fields. + * + * Recording Workers. Workers are recorded in the "workers" array + * that is created upon pool construction and expanded if (rarely) + * necessary. This is an array as opposed to some other data + * structure to support index-based random steals by workers. + * Updates to the array recording new workers and unrecording + * terminated ones are protected from each other by a seqLock + * (scanGuard) but the array is otherwise concurrently readable, + * and accessed directly by workers. To simplify index-based + * operations, the array size is always a power of two, and all + * readers must tolerate null slots. To avoid flailing during + * start-up, the array is presized to hold twice #parallelism + * workers (which is unlikely to need further resizing during + * execution). But to avoid dealing with so many null slots, + * variable scanGuard includes a mask for the nearest power of two + * that contains all current workers. All worker thread creation + * is on-demand, triggered by task submissions, replacement of + * terminated workers, and/or compensation for blocked + * workers. However, all other support code is set up to work with + * other policies. To ensure that we do not hold on to worker + * references that would prevent GC, ALL accesses to workers are + * via indices into the workers array (which is one source of some + * of the messy code constructions here). In essence, the workers + * array serves as a weak reference mechanism. Thus for example + * the wait queue field of ctl stores worker indices, not worker + * references. Access to the workers in associated methods (for + * example signalWork) must both index-check and null-check the + * IDs. All such accesses ignore bad IDs by returning out early + * from what they are doing, since this can only be associated + * with termination, in which case it is OK to give up. + * + * All uses of the workers array, as well as queue arrays, check + * that the array is non-null (even if previously non-null). This + * allows nulling during termination, which is currently not + * necessary, but remains an option for resource-revocation-based + * shutdown schemes. + * + * Wait Queuing. Unlike HPC work-stealing frameworks, we cannot + * let workers spin indefinitely scanning for tasks when none can + * be found immediately, and we cannot start/resume workers unless + * there appear to be tasks available. On the other hand, we must + * quickly prod them into action when new tasks are submitted or + * generated. We park/unpark workers after placing in an event + * wait queue when they cannot find work. This "queue" is actually + * a simple Treiber stack, headed by the "id" field of ctl, plus a + * 15bit counter value to both wake up waiters (by advancing their + * count) and avoid ABA effects. Successors are held in worker + * field "nextWait". Queuing deals with several intrinsic races, + * mainly that a task-producing thread can miss seeing (and + * signalling) another thread that gave up looking for work but + * has not yet entered the wait queue. We solve this by requiring + * a full sweep of all workers both before (in scan()) and after + * (in tryAwaitWork()) a newly waiting worker is added to the wait + * queue. During a rescan, the worker might release some other + * queued worker rather than itself, which has the same net + * effect. Because enqueued workers may actually be rescanning + * rather than waiting, we set and clear the "parked" field of + * ForkJoinWorkerThread to reduce unnecessary calls to unpark. + * (Use of the parked field requires a secondary recheck to avoid + * missed signals.) + * + * Signalling. We create or wake up workers only when there + * appears to be at least one task they might be able to find and + * execute. When a submission is added or another worker adds a + * task to a queue that previously had two or fewer tasks, they + * signal waiting workers (or trigger creation of new ones if + * fewer than the given parallelism level -- see signalWork). + * These primary signals are buttressed by signals during rescans + * as well as those performed when a worker steals a task and + * notices that there are more tasks too; together these cover the + * signals needed in cases when more than two tasks are pushed + * but untaken. + * + * Trimming workers. To release resources after periods of lack of + * use, a worker starting to wait when the pool is quiescent will + * time out and terminate if the pool has remained quiescent for + * SHRINK_RATE nanosecs. This will slowly propagate, eventually + * terminating all workers after long periods of non-use. + * + * Submissions. External submissions are maintained in an + * array-based queue that is structured identically to + * ForkJoinWorkerThread queues except for the use of + * submissionLock in method addSubmission. Unlike the case for + * worker queues, multiple external threads can add new + * submissions, so adding requires a lock. + * + * Compensation. Beyond work-stealing support and lifecycle + * control, the main responsibility of this framework is to take + * actions when one worker is waiting to join a task stolen (or + * always held by) another. Because we are multiplexing many + * tasks on to a pool of workers, we can't just let them block (as + * in Thread.join). We also cannot just reassign the joiner's + * run-time stack with another and replace it later, which would + * be a form of "continuation", that even if possible is not + * necessarily a good idea since we sometimes need both an + * unblocked task and its continuation to progress. Instead we + * combine two tactics: + * + * Helping: Arranging for the joiner to execute some task that it + * would be running if the steal had not occurred. Method + * ForkJoinWorkerThread.joinTask tracks joining->stealing + * links to try to find such a task. + * + * Compensating: Unless there are already enough live threads, + * method tryPreBlock() may create or re-activate a spare + * thread to compensate for blocked joiners until they + * unblock. + * + * The ManagedBlocker extension API can't use helping so relies + * only on compensation in method awaitBlocker. + * + * It is impossible to keep exactly the target parallelism number + * of threads running at any given time. Determining the + * existence of conservatively safe helping targets, the + * availability of already-created spares, and the apparent need + * to create new spares are all racy and require heuristic + * guidance, so we rely on multiple retries of each. Currently, + * in keeping with on-demand signalling policy, we compensate only + * if blocking would leave less than one active (non-waiting, + * non-blocked) worker. Additionally, to avoid some false alarms + * due to GC, lagging counters, system activity, etc, compensated + * blocking for joins is only attempted after rechecks stabilize + * (retries are interspersed with Thread.yield, for good + * citizenship). The variable blockedCount, incremented before + * blocking and decremented after, is sometimes needed to + * distinguish cases of waiting for work vs blocking on joins or + * other managed sync. Both cases are equivalent for most pool + * control, so we can update non-atomically. (Additionally, + * contention on blockedCount alleviates some contention on ctl). + * + * Shutdown and Termination. A call to shutdownNow atomically sets + * the ctl stop bit and then (non-atomically) sets each workers + * "terminate" status, cancels all unprocessed tasks, and wakes up + * all waiting workers. Detecting whether termination should + * commence after a non-abrupt shutdown() call requires more work + * and bookkeeping. We need consensus about quiescence (i.e., that + * there is no more work) which is reflected in active counts so + * long as there are no current blockers, as well as possible + * re-evaluations during independent changes in blocking or + * quiescing workers. + * + * Style notes: There is a lot of representation-level coupling + * among classes ForkJoinPool, ForkJoinWorkerThread, and + * ForkJoinTask. Most fields of ForkJoinWorkerThread maintain + * data structures managed by ForkJoinPool, so are directly + * accessed. Conversely we allow access to "workers" array by + * workers, and direct access to ForkJoinTask.status by both + * ForkJoinPool and ForkJoinWorkerThread. There is little point + * trying to reduce this, since any associated future changes in + * representations will need to be accompanied by algorithmic + * changes anyway. All together, these low-level implementation + * choices produce as much as a factor of 4 performance + * improvement compared to naive implementations, and enable the + * processing of billions of tasks per second, at the expense of + * some ugliness. + * + * Methods signalWork() and scan() are the main bottlenecks so are + * especially heavily micro-optimized/mangled. There are lots of + * inline assignments (of form "while ((local = field) != 0)") + * which are usually the simplest way to ensure the required read + * orderings (which are sometimes critical). This leads to a + * "C"-like style of listing declarations of these locals at the + * heads of methods or blocks. There are several occurrences of + * the unusual "do {} while (!cas...)" which is the simplest way + * to force an update of a CAS'ed variable. There are also other + * coding oddities that help some methods perform reasonably even + * when interpreted (not compiled). + * + * The order of declarations in this file is: (1) declarations of + * statics (2) fields (along with constants used when unpacking + * some of them), listed in an order that tends to reduce + * contention among them a bit under most JVMs. (3) internal + * control methods (4) callbacks and other support for + * ForkJoinTask and ForkJoinWorkerThread classes, (5) exported + * methods (plus a few little helpers). (6) static block + * initializing all statics in a minimally dependent order. + */ + + /** + * Factory for creating new {@link ForkJoinWorkerThread}s. + * A {@code ForkJoinWorkerThreadFactory} must be defined and used + * for {@code ForkJoinWorkerThread} subclasses that extend base + * functionality or initialize threads with different contexts. + */ + public static interface ForkJoinWorkerThreadFactory { + /** + * Returns a new worker thread operating in the given pool. + * + * @param pool the pool this thread works in + * @throws NullPointerException if the pool is null + */ + public ForkJoinWorkerThread newThread(ForkJoinPool pool); + } + + /** + * Default ForkJoinWorkerThreadFactory implementation; creates a + * new ForkJoinWorkerThread. + */ + static class DefaultForkJoinWorkerThreadFactory + implements ForkJoinWorkerThreadFactory { + public ForkJoinWorkerThread newThread(ForkJoinPool pool) { + return new ForkJoinWorkerThread(pool); + } + } + + /** + * Creates a new ForkJoinWorkerThread. This factory is used unless + * overridden in ForkJoinPool constructors. + */ + public static final ForkJoinWorkerThreadFactory + defaultForkJoinWorkerThreadFactory; + + /** + * Permission required for callers of methods that may start or + * kill threads. + */ + private static final RuntimePermission modifyThreadPermission; + + /** + * If there is a security manager, makes sure caller has + * permission to modify threads. + */ + private static void checkPermission() { + SecurityManager security = System.getSecurityManager(); + if (security != null) + security.checkPermission(modifyThreadPermission); + } + + /** + * Generator for assigning sequence numbers as pool names. + */ + private static final AtomicInteger poolNumberGenerator; + + /** + * Generator for initial random seeds for worker victim + * selection. This is used only to create initial seeds. Random + * steals use a cheaper xorshift generator per steal attempt. We + * don't expect much contention on seedGenerator, so just use a + * plain Random. + */ + static final Random workerSeedGenerator; + + /** + * Array holding all worker threads in the pool. Initialized upon + * construction. Array size must be a power of two. Updates and + * replacements are protected by scanGuard, but the array is + * always kept in a consistent enough state to be randomly + * accessed without locking by workers performing work-stealing, + * as well as other traversal-based methods in this class, so long + * as reads memory-acquire by first reading ctl. All readers must + * tolerate that some array slots may be null. + */ + ForkJoinWorkerThread[] workers; + + /** + * Initial size for submission queue array. Must be a power of + * two. In many applications, these always stay small so we use a + * small initial cap. + */ + private static final int INITIAL_QUEUE_CAPACITY = 8; + + /** + * Maximum size for submission queue array. Must be a power of two + * less than or equal to 1 << (31 - width of array entry) to + * ensure lack of index wraparound, but is capped at a lower + * value to help users trap runaway computations. + */ + private static final int MAXIMUM_QUEUE_CAPACITY = 1 << 24; // 16M + + /** + * Array serving as submission queue. Initialized upon construction. + */ + private ForkJoinTask<?>[] submissionQueue; + + /** + * Lock protecting submissions array for addSubmission + */ + private final ReentrantLock submissionLock; + + /** + * Condition for awaitTermination, using submissionLock for + * convenience. + */ + private final Condition termination; + + /** + * Creation factory for worker threads. + */ + private final ForkJoinWorkerThreadFactory factory; + + /** + * The uncaught exception handler used when any worker abruptly + * terminates. + */ + final Thread.UncaughtExceptionHandler ueh; + + /** + * Prefix for assigning names to worker threads + */ + private final String workerNamePrefix; + + /** + * Sum of per-thread steal counts, updated only when threads are + * idle or terminating. + */ + private volatile long stealCount; + + /** + * Main pool control -- a long packed with: + * AC: Number of active running workers minus target parallelism (16 bits) + * TC: Number of total workers minus target parallelism (16 bits) + * ST: true if pool is terminating (1 bit) + * EC: the wait count of top waiting thread (15 bits) + * ID: ~poolIndex of top of Treiber stack of waiting threads (16 bits) + * + * When convenient, we can extract the upper 32 bits of counts and + * the lower 32 bits of queue state, u = (int)(ctl >>> 32) and e = + * (int)ctl. The ec field is never accessed alone, but always + * together with id and st. The offsets of counts by the target + * parallelism and the positionings of fields makes it possible to + * perform the most common checks via sign tests of fields: When + * ac is negative, there are not enough active workers, when tc is + * negative, there are not enough total workers, when id is + * negative, there is at least one waiting worker, and when e is + * negative, the pool is terminating. To deal with these possibly + * negative fields, we use casts in and out of "short" and/or + * signed shifts to maintain signedness. + */ + volatile long ctl; + + // bit positions/shifts for fields + private static final int AC_SHIFT = 48; + private static final int TC_SHIFT = 32; + private static final int ST_SHIFT = 31; + private static final int EC_SHIFT = 16; + + // bounds + private static final int MAX_ID = 0x7fff; // max poolIndex + private static final int SMASK = 0xffff; // mask short bits + private static final int SHORT_SIGN = 1 << 15; + private static final int INT_SIGN = 1 << 31; + + // masks + private static final long STOP_BIT = 0x0001L << ST_SHIFT; + private static final long AC_MASK = ((long)SMASK) << AC_SHIFT; + private static final long TC_MASK = ((long)SMASK) << TC_SHIFT; + + // units for incrementing and decrementing + private static final long TC_UNIT = 1L << TC_SHIFT; + private static final long AC_UNIT = 1L << AC_SHIFT; + + // masks and units for dealing with u = (int)(ctl >>> 32) + private static final int UAC_SHIFT = AC_SHIFT - 32; + private static final int UTC_SHIFT = TC_SHIFT - 32; + private static final int UAC_MASK = SMASK << UAC_SHIFT; + private static final int UTC_MASK = SMASK << UTC_SHIFT; + private static final int UAC_UNIT = 1 << UAC_SHIFT; + private static final int UTC_UNIT = 1 << UTC_SHIFT; + + // masks and units for dealing with e = (int)ctl + private static final int E_MASK = 0x7fffffff; // no STOP_BIT + private static final int EC_UNIT = 1 << EC_SHIFT; + + /** + * The target parallelism level. + */ + final int parallelism; + + /** + * Index (mod submission queue length) of next element to take + * from submission queue. Usage is identical to that for + * per-worker queues -- see ForkJoinWorkerThread internal + * documentation. + */ + volatile int queueBase; + + /** + * Index (mod submission queue length) of next element to add + * in submission queue. Usage is identical to that for + * per-worker queues -- see ForkJoinWorkerThread internal + * documentation. + */ + int queueTop; + + /** + * True when shutdown() has been called. + */ + volatile boolean shutdown; + + /** + * True if use local fifo, not default lifo, for local polling. + * Read by, and replicated by ForkJoinWorkerThreads. + */ + final boolean locallyFifo; + + /** + * The number of threads in ForkJoinWorkerThreads.helpQuiescePool. + * When non-zero, suppresses automatic shutdown when active + * counts become zero. + */ + volatile int quiescerCount; + + /** + * The number of threads blocked in join. + */ + volatile int blockedCount; + + /** + * Counter for worker Thread names (unrelated to their poolIndex) + */ + private volatile int nextWorkerNumber; + + /** + * The index for the next created worker. Accessed under scanGuard. + */ + private int nextWorkerIndex; + + /** + * SeqLock and index masking for updates to workers array. Locked + * when SG_UNIT is set. Unlocking clears bit by adding + * SG_UNIT. Staleness of read-only operations can be checked by + * comparing scanGuard to value before the reads. The low 16 bits + * (i.e, anding with SMASK) hold (the smallest power of two + * covering all worker indices, minus one, and is used to avoid + * dealing with large numbers of null slots when the workers array + * is overallocated. + */ + volatile int scanGuard; + + private static final int SG_UNIT = 1 << 16; + + /** + * The wakeup interval (in nanoseconds) for a worker waiting for a + * task when the pool is quiescent to instead try to shrink the + * number of workers. The exact value does not matter too + * much. It must be short enough to release resources during + * sustained periods of idleness, but not so short that threads + * are continually re-created. + */ + private static final long SHRINK_RATE = + 4L * 1000L * 1000L * 1000L; // 4 seconds + + /** + * Top-level loop for worker threads: On each step: if the + * previous step swept through all queues and found no tasks, or + * there are excess threads, then possibly blocks. Otherwise, + * scans for and, if found, executes a task. Returns when pool + * and/or worker terminate. + * + * @param w the worker + */ + final void work(ForkJoinWorkerThread w) { + boolean swept = false; // true on empty scans + long c; + while (!w.terminate && (int)(c = ctl) >= 0) { + int a; // active count + if (!swept && (a = (int)(c >> AC_SHIFT)) <= 0) + swept = scan(w, a); + else if (tryAwaitWork(w, c)) + swept = false; + } + } + + // Signalling + + /** + * Wakes up or creates a worker. + */ + final void signalWork() { + /* + * The while condition is true if: (there is are too few total + * workers OR there is at least one waiter) AND (there are too + * few active workers OR the pool is terminating). The value + * of e distinguishes the remaining cases: zero (no waiters) + * for create, negative if terminating (in which case do + * nothing), else release a waiter. The secondary checks for + * release (non-null array etc) can fail if the pool begins + * terminating after the test, and don't impose any added cost + * because JVMs must perform null and bounds checks anyway. + */ + long c; int e, u; + while ((((e = (int)(c = ctl)) | (u = (int)(c >>> 32))) & + (INT_SIGN|SHORT_SIGN)) == (INT_SIGN|SHORT_SIGN) && e >= 0) { + if (e > 0) { // release a waiting worker + int i; ForkJoinWorkerThread w; ForkJoinWorkerThread[] ws; + if ((ws = workers) == null || + (i = ~e & SMASK) >= ws.length || + (w = ws[i]) == null) + break; + long nc = (((long)(w.nextWait & E_MASK)) | + ((long)(u + UAC_UNIT) << 32)); + if (w.eventCount == e && + UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc)) { + w.eventCount = (e + EC_UNIT) & E_MASK; + if (w.parked) + UNSAFE.unpark(w); + break; + } + } + else if (UNSAFE.compareAndSwapLong + (this, ctlOffset, c, + (long)(((u + UTC_UNIT) & UTC_MASK) | + ((u + UAC_UNIT) & UAC_MASK)) << 32)) { + addWorker(); + break; + } + } + } + + /** + * Variant of signalWork to help release waiters on rescans. + * Tries once to release a waiter if active count < 0. + * + * @return false if failed due to contention, else true + */ + private boolean tryReleaseWaiter() { + long c; int e, i; ForkJoinWorkerThread w; ForkJoinWorkerThread[] ws; + if ((e = (int)(c = ctl)) > 0 && + (int)(c >> AC_SHIFT) < 0 && + (ws = workers) != null && + (i = ~e & SMASK) < ws.length && + (w = ws[i]) != null) { + long nc = ((long)(w.nextWait & E_MASK) | + ((c + AC_UNIT) & (AC_MASK|TC_MASK))); + if (w.eventCount != e || + !UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc)) + return false; + w.eventCount = (e + EC_UNIT) & E_MASK; + if (w.parked) + UNSAFE.unpark(w); + } + return true; + } + + // Scanning for tasks + + /** + * Scans for and, if found, executes one task. Scans start at a + * random index of workers array, and randomly select the first + * (2*#workers)-1 probes, and then, if all empty, resort to 2 + * circular sweeps, which is necessary to check quiescence. and + * taking a submission only if no stealable tasks were found. The + * steal code inside the loop is a specialized form of + * ForkJoinWorkerThread.deqTask, followed bookkeeping to support + * helpJoinTask and signal propagation. The code for submission + * queues is almost identical. On each steal, the worker completes + * not only the task, but also all local tasks that this task may + * have generated. On detecting staleness or contention when + * trying to take a task, this method returns without finishing + * sweep, which allows global state rechecks before retry. + * + * @param w the worker + * @param a the number of active workers + * @return true if swept all queues without finding a task + */ + private boolean scan(ForkJoinWorkerThread w, int a) { + int g = scanGuard; // mask 0 avoids useless scans if only one active + int m = (parallelism == 1 - a && blockedCount == 0) ? 0 : g & SMASK; + ForkJoinWorkerThread[] ws = workers; + if (ws == null || ws.length <= m) // staleness check + return false; + for (int r = w.seed, k = r, j = -(m + m); j <= m + m; ++j) { + ForkJoinTask<?> t; ForkJoinTask<?>[] q; int b, i; + ForkJoinWorkerThread v = ws[k & m]; + if (v != null && (b = v.queueBase) != v.queueTop && + (q = v.queue) != null && (i = (q.length - 1) & b) >= 0) { + long u = (i << ASHIFT) + ABASE; + if ((t = q[i]) != null && v.queueBase == b && + UNSAFE.compareAndSwapObject(q, u, t, null)) { + int d = (v.queueBase = b + 1) - v.queueTop; + v.stealHint = w.poolIndex; + if (d != 0) + signalWork(); // propagate if nonempty + w.execTask(t); + } + r ^= r << 13; r ^= r >>> 17; w.seed = r ^ (r << 5); + return false; // store next seed + } + else if (j < 0) { // xorshift + r ^= r << 13; r ^= r >>> 17; k = r ^= r << 5; + } + else + ++k; + } + if (scanGuard != g) // staleness check + return false; + else { // try to take submission + ForkJoinTask<?> t; ForkJoinTask<?>[] q; int b, i; + if ((b = queueBase) != queueTop && + (q = submissionQueue) != null && + (i = (q.length - 1) & b) >= 0) { + long u = (i << ASHIFT) + ABASE; + if ((t = q[i]) != null && queueBase == b && + UNSAFE.compareAndSwapObject(q, u, t, null)) { + queueBase = b + 1; + w.execTask(t); + } + return false; + } + return true; // all queues empty + } + } + + /** + * Tries to enqueue worker w in wait queue and await change in + * worker's eventCount. If the pool is quiescent and there is + * more than one worker, possibly terminates worker upon exit. + * Otherwise, before blocking, rescans queues to avoid missed + * signals. Upon finding work, releases at least one worker + * (which may be the current worker). Rescans restart upon + * detected staleness or failure to release due to + * contention. Note the unusual conventions about Thread.interrupt + * here and elsewhere: Because interrupts are used solely to alert + * threads to check termination, which is checked here anyway, we + * clear status (using Thread.interrupted) before any call to + * park, so that park does not immediately return due to status + * being set via some other unrelated call to interrupt in user + * code. + * + * @param w the calling worker + * @param c the ctl value on entry + * @return true if waited or another thread was released upon enq + */ + private boolean tryAwaitWork(ForkJoinWorkerThread w, long c) { + int v = w.eventCount; + w.nextWait = (int)c; // w's successor record + long nc = (long)(v & E_MASK) | ((c - AC_UNIT) & (AC_MASK|TC_MASK)); + if (ctl != c || !UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc)) { + long d = ctl; // return true if lost to a deq, to force scan + return (int)d != (int)c && (d & AC_MASK) >= (c & AC_MASK); + } + for (int sc = w.stealCount; sc != 0;) { // accumulate stealCount + long s = stealCount; + if (UNSAFE.compareAndSwapLong(this, stealCountOffset, s, s + sc)) + sc = w.stealCount = 0; + else if (w.eventCount != v) + return true; // update next time + } + if ((!shutdown || !tryTerminate(false)) && + (int)c != 0 && parallelism + (int)(nc >> AC_SHIFT) == 0 && + blockedCount == 0 && quiescerCount == 0) + idleAwaitWork(w, nc, c, v); // quiescent + for (boolean rescanned = false;;) { + if (w.eventCount != v) + return true; + if (!rescanned) { + int g = scanGuard, m = g & SMASK; + ForkJoinWorkerThread[] ws = workers; + if (ws != null && m < ws.length) { + rescanned = true; + for (int i = 0; i <= m; ++i) { + ForkJoinWorkerThread u = ws[i]; + if (u != null) { + if (u.queueBase != u.queueTop && + !tryReleaseWaiter()) + rescanned = false; // contended + if (w.eventCount != v) + return true; + } + } + } + if (scanGuard != g || // stale + (queueBase != queueTop && !tryReleaseWaiter())) + rescanned = false; + if (!rescanned) + Thread.yield(); // reduce contention + else + Thread.interrupted(); // clear before park + } + else { + w.parked = true; // must recheck + if (w.eventCount != v) { + w.parked = false; + return true; + } + LockSupport.park(this); + rescanned = w.parked = false; + } + } + } + + /** + * If inactivating worker w has caused pool to become + * quiescent, check for pool termination, and wait for event + * for up to SHRINK_RATE nanosecs (rescans are unnecessary in + * this case because quiescence reflects consensus about lack + * of work). On timeout, if ctl has not changed, terminate the + * worker. Upon its termination (see deregisterWorker), it may + * wake up another worker to possibly repeat this process. + * + * @param w the calling worker + * @param currentCtl the ctl value after enqueuing w + * @param prevCtl the ctl value if w terminated + * @param v the eventCount w awaits change + */ + private void idleAwaitWork(ForkJoinWorkerThread w, long currentCtl, + long prevCtl, int v) { + if (w.eventCount == v) { + if (shutdown) + tryTerminate(false); + ForkJoinTask.helpExpungeStaleExceptions(); // help clean weak refs + while (ctl == currentCtl) { + long startTime = System.nanoTime(); + w.parked = true; + if (w.eventCount == v) // must recheck + LockSupport.parkNanos(this, SHRINK_RATE); + w.parked = false; + if (w.eventCount != v) + break; + else if (System.nanoTime() - startTime < + SHRINK_RATE - (SHRINK_RATE / 10)) // timing slop + Thread.interrupted(); // spurious wakeup + else if (UNSAFE.compareAndSwapLong(this, ctlOffset, + currentCtl, prevCtl)) { + w.terminate = true; // restore previous + w.eventCount = ((int)currentCtl + EC_UNIT) & E_MASK; + break; + } + } + } + } + + // Submissions + + /** + * Enqueues the given task in the submissionQueue. Same idea as + * ForkJoinWorkerThread.pushTask except for use of submissionLock. + * + * @param t the task + */ + private void addSubmission(ForkJoinTask<?> t) { + final ReentrantLock lock = this.submissionLock; + lock.lock(); + try { + ForkJoinTask<?>[] q; int s, m; + if ((q = submissionQueue) != null) { // ignore if queue removed + long u = (((s = queueTop) & (m = q.length-1)) << ASHIFT)+ABASE; + UNSAFE.putOrderedObject(q, u, t); + queueTop = s + 1; + if (s - queueBase == m) + growSubmissionQueue(); + } + } finally { + lock.unlock(); + } + signalWork(); + } + + // (pollSubmission is defined below with exported methods) + + /** + * Creates or doubles submissionQueue array. + * Basically identical to ForkJoinWorkerThread version. + */ + private void growSubmissionQueue() { + ForkJoinTask<?>[] oldQ = submissionQueue; + int size = oldQ != null ? oldQ.length << 1 : INITIAL_QUEUE_CAPACITY; + if (size > MAXIMUM_QUEUE_CAPACITY) + throw new RejectedExecutionException("Queue capacity exceeded"); + if (size < INITIAL_QUEUE_CAPACITY) + size = INITIAL_QUEUE_CAPACITY; + ForkJoinTask<?>[] q = submissionQueue = new ForkJoinTask<?>[size]; + int mask = size - 1; + int top = queueTop; + int oldMask; + if (oldQ != null && (oldMask = oldQ.length - 1) >= 0) { + for (int b = queueBase; b != top; ++b) { + long u = ((b & oldMask) << ASHIFT) + ABASE; + Object x = UNSAFE.getObjectVolatile(oldQ, u); + if (x != null && UNSAFE.compareAndSwapObject(oldQ, u, x, null)) + UNSAFE.putObjectVolatile + (q, ((b & mask) << ASHIFT) + ABASE, x); + } + } + } + + // Blocking support + + /** + * Tries to increment blockedCount, decrement active count + * (sometimes implicitly) and possibly release or create a + * compensating worker in preparation for blocking. Fails + * on contention or termination. + * + * @return true if the caller can block, else should recheck and retry + */ + private boolean tryPreBlock() { + int b = blockedCount; + if (UNSAFE.compareAndSwapInt(this, blockedCountOffset, b, b + 1)) { + int pc = parallelism; + do { + ForkJoinWorkerThread[] ws; ForkJoinWorkerThread w; + int e, ac, tc, i; + long c = ctl; + int u = (int)(c >>> 32); + if ((e = (int)c) < 0) { + // skip -- terminating + } + else if ((ac = (u >> UAC_SHIFT)) <= 0 && e != 0 && + (ws = workers) != null && + (i = ~e & SMASK) < ws.length && + (w = ws[i]) != null) { + long nc = ((long)(w.nextWait & E_MASK) | + (c & (AC_MASK|TC_MASK))); + if (w.eventCount == e && + UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc)) { + w.eventCount = (e + EC_UNIT) & E_MASK; + if (w.parked) + UNSAFE.unpark(w); + return true; // release an idle worker + } + } + else if ((tc = (short)(u >>> UTC_SHIFT)) >= 0 && ac + pc > 1) { + long nc = ((c - AC_UNIT) & AC_MASK) | (c & ~AC_MASK); + if (UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc)) + return true; // no compensation needed + } + else if (tc + pc < MAX_ID) { + long nc = ((c + TC_UNIT) & TC_MASK) | (c & ~TC_MASK); + if (UNSAFE.compareAndSwapLong(this, ctlOffset, c, nc)) { + addWorker(); + return true; // create a replacement + } + } + // try to back out on any failure and let caller retry + } while (!UNSAFE.compareAndSwapInt(this, blockedCountOffset, + b = blockedCount, b - 1)); + } + return false; + } + + /** + * Decrements blockedCount and increments active count. + */ + private void postBlock() { + long c; + do {} while (!UNSAFE.compareAndSwapLong(this, ctlOffset, // no mask + c = ctl, c + AC_UNIT)); + int b; + do {} while (!UNSAFE.compareAndSwapInt(this, blockedCountOffset, + b = blockedCount, b - 1)); + } + + /** + * Possibly blocks waiting for the given task to complete, or + * cancels the task if terminating. Fails to wait if contended. + * + * @param joinMe the task + */ + final void tryAwaitJoin(ForkJoinTask<?> joinMe) { + Thread.interrupted(); // clear interrupts before checking termination + if (joinMe.status >= 0) { + if (tryPreBlock()) { + joinMe.tryAwaitDone(0L); + postBlock(); + } + else if ((ctl & STOP_BIT) != 0L) + joinMe.cancelIgnoringExceptions(); + } + } + + /** + * Possibly blocks the given worker waiting for joinMe to + * complete or timeout. + * + * @param joinMe the task + * @param nanos the wait time for underlying Object.wait + */ + final void timedAwaitJoin(ForkJoinTask<?> joinMe, long nanos) { + while (joinMe.status >= 0) { + Thread.interrupted(); + if ((ctl & STOP_BIT) != 0L) { + joinMe.cancelIgnoringExceptions(); + break; + } + if (tryPreBlock()) { + long last = System.nanoTime(); + while (joinMe.status >= 0) { + long millis = TimeUnit.NANOSECONDS.toMillis(nanos); + if (millis <= 0) + break; + joinMe.tryAwaitDone(millis); + if (joinMe.status < 0) + break; + if ((ctl & STOP_BIT) != 0L) { + joinMe.cancelIgnoringExceptions(); + break; + } + long now = System.nanoTime(); + nanos -= now - last; + last = now; + } + postBlock(); + break; + } + } + } + + /** + * If necessary, compensates for blocker, and blocks. + */ + private void awaitBlocker(ManagedBlocker blocker) + throws InterruptedException { + while (!blocker.isReleasable()) { + if (tryPreBlock()) { + try { + do {} while (!blocker.isReleasable() && !blocker.block()); + } finally { + postBlock(); + } + break; + } + } + } + + // Creating, registering and deregistring workers + + /** + * Tries to create and start a worker; minimally rolls back counts + * on failure. + */ + private void addWorker() { + Throwable ex = null; + ForkJoinWorkerThread t = null; + try { + t = factory.newThread(this); + } catch (Throwable e) { + ex = e; + } + if (t == null) { // null or exceptional factory return + long c; // adjust counts + do {} while (!UNSAFE.compareAndSwapLong + (this, ctlOffset, c = ctl, + (((c - AC_UNIT) & AC_MASK) | + ((c - TC_UNIT) & TC_MASK) | + (c & ~(AC_MASK|TC_MASK))))); + // Propagate exception if originating from an external caller + if (!tryTerminate(false) && ex != null && + !(Thread.currentThread() instanceof ForkJoinWorkerThread)) + SneakyThrow.sneakyThrow(ex); // android-changed + } + else + t.start(); + } + + /** + * Callback from ForkJoinWorkerThread constructor to assign a + * public name + */ + final String nextWorkerName() { + for (int n;;) { + if (UNSAFE.compareAndSwapInt(this, nextWorkerNumberOffset, + n = nextWorkerNumber, ++n)) + return workerNamePrefix + n; + } + } + + /** + * Callback from ForkJoinWorkerThread constructor to + * determine its poolIndex and record in workers array. + * + * @param w the worker + * @return the worker's pool index + */ + final int registerWorker(ForkJoinWorkerThread w) { + /* + * In the typical case, a new worker acquires the lock, uses + * next available index and returns quickly. Since we should + * not block callers (ultimately from signalWork or + * tryPreBlock) waiting for the lock needed to do this, we + * instead help release other workers while waiting for the + * lock. + */ + for (int g;;) { + ForkJoinWorkerThread[] ws; + if (((g = scanGuard) & SG_UNIT) == 0 && + UNSAFE.compareAndSwapInt(this, scanGuardOffset, + g, g | SG_UNIT)) { + int k = nextWorkerIndex; + try { + if ((ws = workers) != null) { // ignore on shutdown + int n = ws.length; + if (k < 0 || k >= n || ws[k] != null) { + for (k = 0; k < n && ws[k] != null; ++k) + ; + if (k == n) + ws = workers = Arrays.copyOf(ws, n << 1); + } + ws[k] = w; + nextWorkerIndex = k + 1; + int m = g & SMASK; + g = (k > m) ? ((m << 1) + 1) & SMASK : g + (SG_UNIT<<1); + } + } finally { + scanGuard = g; + } + return k; + } + else if ((ws = workers) != null) { // help release others + for (ForkJoinWorkerThread u : ws) { + if (u != null && u.queueBase != u.queueTop) { + if (tryReleaseWaiter()) + break; + } + } + } + } + } + + /** + * Final callback from terminating worker. Removes record of + * worker from array, and adjusts counts. If pool is shutting + * down, tries to complete termination. + * + * @param w the worker + */ + final void deregisterWorker(ForkJoinWorkerThread w, Throwable ex) { + int idx = w.poolIndex; + int sc = w.stealCount; + int steps = 0; + // Remove from array, adjust worker counts and collect steal count. + // We can intermix failed removes or adjusts with steal updates + do { + long s, c; + int g; + if (steps == 0 && ((g = scanGuard) & SG_UNIT) == 0 && + UNSAFE.compareAndSwapInt(this, scanGuardOffset, + g, g |= SG_UNIT)) { + ForkJoinWorkerThread[] ws = workers; + if (ws != null && idx >= 0 && + idx < ws.length && ws[idx] == w) + ws[idx] = null; // verify + nextWorkerIndex = idx; + scanGuard = g + SG_UNIT; + steps = 1; + } + if (steps == 1 && + UNSAFE.compareAndSwapLong(this, ctlOffset, c = ctl, + (((c - AC_UNIT) & AC_MASK) | + ((c - TC_UNIT) & TC_MASK) | + (c & ~(AC_MASK|TC_MASK))))) + steps = 2; + if (sc != 0 && + UNSAFE.compareAndSwapLong(this, stealCountOffset, + s = stealCount, s + sc)) + sc = 0; + } while (steps != 2 || sc != 0); + if (!tryTerminate(false)) { + if (ex != null) // possibly replace if died abnormally + signalWork(); + else + tryReleaseWaiter(); + } + } + + // Shutdown and termination + + /** + * Possibly initiates and/or completes termination. + * + * @param now if true, unconditionally terminate, else only + * if shutdown and empty queue and no active workers + * @return true if now terminating or terminated + */ + private boolean tryTerminate(boolean now) { + long c; + while (((c = ctl) & STOP_BIT) == 0) { + if (!now) { + if ((int)(c >> AC_SHIFT) != -parallelism) + return false; + if (!shutdown || blockedCount != 0 || quiescerCount != 0 || + queueBase != queueTop) { + if (ctl == c) // staleness check + return false; + continue; + } + } + if (UNSAFE.compareAndSwapLong(this, ctlOffset, c, c | STOP_BIT)) + startTerminating(); + } + if ((short)(c >>> TC_SHIFT) == -parallelism) { // signal when 0 workers + final ReentrantLock lock = this.submissionLock; + lock.lock(); + try { + termination.signalAll(); + } finally { + lock.unlock(); + } + } + return true; + } + + /** + * Runs up to three passes through workers: (0) Setting + * termination status for each worker, followed by wakeups up to + * queued workers; (1) helping cancel tasks; (2) interrupting + * lagging threads (likely in external tasks, but possibly also + * blocked in joins). Each pass repeats previous steps because of + * potential lagging thread creation. + */ + private void startTerminating() { + cancelSubmissions(); + for (int pass = 0; pass < 3; ++pass) { + ForkJoinWorkerThread[] ws = workers; + if (ws != null) { + for (ForkJoinWorkerThread w : ws) { + if (w != null) { + w.terminate = true; + if (pass > 0) { + w.cancelTasks(); + if (pass > 1 && !w.isInterrupted()) { + try { + w.interrupt(); + } catch (SecurityException ignore) { + } + } + } + } + } + terminateWaiters(); + } + } + } + + /** + * Polls and cancels all submissions. Called only during termination. + */ + private void cancelSubmissions() { + while (queueBase != queueTop) { + ForkJoinTask<?> task = pollSubmission(); + if (task != null) { + try { + task.cancel(false); + } catch (Throwable ignore) { + } + } + } + } + + /** + * Tries to set the termination status of waiting workers, and + * then wakes them up (after which they will terminate). + */ + private void terminateWaiters() { + ForkJoinWorkerThread[] ws = workers; + if (ws != null) { + ForkJoinWorkerThread w; long c; int i, e; + int n = ws.length; + while ((i = ~(e = (int)(c = ctl)) & SMASK) < n && + (w = ws[i]) != null && w.eventCount == (e & E_MASK)) { + if (UNSAFE.compareAndSwapLong(this, ctlOffset, c, + (long)(w.nextWait & E_MASK) | + ((c + AC_UNIT) & AC_MASK) | + (c & (TC_MASK|STOP_BIT)))) { + w.terminate = true; + w.eventCount = e + EC_UNIT; + if (w.parked) + UNSAFE.unpark(w); + } + } + } + } + + // misc ForkJoinWorkerThread support + + /** + * Increments or decrements quiescerCount. Needed only to prevent + * triggering shutdown if a worker is transiently inactive while + * checking quiescence. + * + * @param delta 1 for increment, -1 for decrement + */ + final void addQuiescerCount(int delta) { + int c; + do {} while (!UNSAFE.compareAndSwapInt(this, quiescerCountOffset, + c = quiescerCount, c + delta)); + } + + /** + * Directly increments or decrements active count without queuing. + * This method is used to transiently assert inactivation while + * checking quiescence. + * + * @param delta 1 for increment, -1 for decrement + */ + final void addActiveCount(int delta) { + long d = (long)delta << AC_SHIFT; + long c; + do {} while (!UNSAFE.compareAndSwapLong(this, ctlOffset, + c = ctl, c + d)); + } + + /** + * Returns the approximate (non-atomic) number of idle threads per + * active thread. + */ + final int idlePerActive() { + // Approximate at powers of two for small values, saturate past 4 + int p = parallelism; + int a = p + (int)(ctl >> AC_SHIFT); + return (a > (p >>>= 1) ? 0 : + a > (p >>>= 1) ? 1 : + a > (p >>>= 1) ? 2 : + a > (p >>>= 1) ? 4 : + 8); + } + + // Exported methods + + // Constructors + + /** + * Creates a {@code ForkJoinPool} with parallelism equal to {@link + * java.lang.Runtime#availableProcessors}, using the {@linkplain + * #defaultForkJoinWorkerThreadFactory default thread factory}, + * no UncaughtExceptionHandler, and non-async LIFO processing mode. + */ + public ForkJoinPool() { + this(Runtime.getRuntime().availableProcessors(), + defaultForkJoinWorkerThreadFactory, null, false); + } + + /** + * Creates a {@code ForkJoinPool} with the indicated parallelism + * level, the {@linkplain + * #defaultForkJoinWorkerThreadFactory default thread factory}, + * no UncaughtExceptionHandler, and non-async LIFO processing mode. + * + * @param parallelism the parallelism level + * @throws IllegalArgumentException if parallelism less than or + * equal to zero, or greater than implementation limit + */ + public ForkJoinPool(int parallelism) { + this(parallelism, defaultForkJoinWorkerThreadFactory, null, false); + } + + /** + * Creates a {@code ForkJoinPool} with the given parameters. + * + * @param parallelism the parallelism level. For default value, + * use {@link java.lang.Runtime#availableProcessors}. + * @param factory the factory for creating new threads. For default value, + * use {@link #defaultForkJoinWorkerThreadFactory}. + * @param handler the handler for internal worker threads that + * terminate due to unrecoverable errors encountered while executing + * tasks. For default value, use {@code null}. + * @param asyncMode if true, + * establishes local first-in-first-out scheduling mode for forked + * tasks that are never joined. This mode may be more appropriate + * than default locally stack-based mode in applications in which + * worker threads only process event-style asynchronous tasks. + * For default value, use {@code false}. + * @throws IllegalArgumentException if parallelism less than or + * equal to zero, or greater than implementation limit + * @throws NullPointerException if the factory is null + */ + public ForkJoinPool(int parallelism, + ForkJoinWorkerThreadFactory factory, + Thread.UncaughtExceptionHandler handler, + boolean asyncMode) { + checkPermission(); + if (factory == null) + throw new NullPointerException(); + if (parallelism <= 0 || parallelism > MAX_ID) + throw new IllegalArgumentException(); + this.parallelism = parallelism; + this.factory = factory; + this.ueh = handler; + this.locallyFifo = asyncMode; + long np = (long)(-parallelism); // offset ctl counts + this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK); + this.submissionQueue = new ForkJoinTask<?>[INITIAL_QUEUE_CAPACITY]; + // initialize workers array with room for 2*parallelism if possible + int n = parallelism << 1; + if (n >= MAX_ID) + n = MAX_ID; + else { // See Hackers Delight, sec 3.2, where n < (1 << 16) + n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; + } + workers = new ForkJoinWorkerThread[n + 1]; + this.submissionLock = new ReentrantLock(); + this.termination = submissionLock.newCondition(); + StringBuilder sb = new StringBuilder("ForkJoinPool-"); + sb.append(poolNumberGenerator.incrementAndGet()); + sb.append("-worker-"); + this.workerNamePrefix = sb.toString(); + } + + // Execution methods + + /** + * Performs the given task, returning its result upon completion. + * If the computation encounters an unchecked Exception or Error, + * it is rethrown as the outcome of this invocation. Rethrown + * exceptions behave in the same way as regular exceptions, but, + * when possible, contain stack traces (as displayed for example + * using {@code ex.printStackTrace()}) of both the current thread + * as well as the thread actually encountering the exception; + * minimally only the latter. + * + * @param task the task + * @return the task's result + * @throws NullPointerException if the task is null + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution + */ + public <T> T invoke(ForkJoinTask<T> task) { + Thread t = Thread.currentThread(); + if (task == null) + throw new NullPointerException(); + if (shutdown) + throw new RejectedExecutionException(); + if ((t instanceof ForkJoinWorkerThread) && + ((ForkJoinWorkerThread)t).pool == this) + return task.invoke(); // bypass submit if in same pool + else { + addSubmission(task); + return task.join(); + } + } + + /** + * Unless terminating, forks task if within an ongoing FJ + * computation in the current pool, else submits as external task. + */ + private <T> void forkOrSubmit(ForkJoinTask<T> task) { + ForkJoinWorkerThread w; + Thread t = Thread.currentThread(); + if (shutdown) + throw new RejectedExecutionException(); + if ((t instanceof ForkJoinWorkerThread) && + (w = (ForkJoinWorkerThread)t).pool == this) + w.pushTask(task); + else + addSubmission(task); + } + + /** + * Arranges for (asynchronous) execution of the given task. + * + * @param task the task + * @throws NullPointerException if the task is null + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution + */ + public void execute(ForkJoinTask<?> task) { + if (task == null) + throw new NullPointerException(); + forkOrSubmit(task); + } + + // AbstractExecutorService methods + + /** + * @throws NullPointerException if the task is null + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution + */ + public void execute(Runnable task) { + if (task == null) + throw new NullPointerException(); + ForkJoinTask<?> job; + if (task instanceof ForkJoinTask<?>) // avoid re-wrap + job = (ForkJoinTask<?>) task; + else + job = ForkJoinTask.adapt(task, null); + forkOrSubmit(job); + } + + /** + * Submits a ForkJoinTask for execution. + * + * @param task the task to submit + * @return the task + * @throws NullPointerException if the task is null + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution + */ + public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task) { + if (task == null) + throw new NullPointerException(); + forkOrSubmit(task); + return task; + } + + /** + * @throws NullPointerException if the task is null + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution + */ + public <T> ForkJoinTask<T> submit(Callable<T> task) { + if (task == null) + throw new NullPointerException(); + ForkJoinTask<T> job = ForkJoinTask.adapt(task); + forkOrSubmit(job); + return job; + } + + /** + * @throws NullPointerException if the task is null + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution + */ + public <T> ForkJoinTask<T> submit(Runnable task, T result) { + if (task == null) + throw new NullPointerException(); + ForkJoinTask<T> job = ForkJoinTask.adapt(task, result); + forkOrSubmit(job); + return job; + } + + /** + * @throws NullPointerException if the task is null + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution + */ + public ForkJoinTask<?> submit(Runnable task) { + if (task == null) + throw new NullPointerException(); + ForkJoinTask<?> job; + if (task instanceof ForkJoinTask<?>) // avoid re-wrap + job = (ForkJoinTask<?>) task; + else + job = ForkJoinTask.adapt(task, null); + forkOrSubmit(job); + return job; + } + + /** + * @throws NullPointerException {@inheritDoc} + * @throws RejectedExecutionException {@inheritDoc} + */ + public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) { + ArrayList<ForkJoinTask<T>> forkJoinTasks = + new ArrayList<ForkJoinTask<T>>(tasks.size()); + for (Callable<T> task : tasks) + forkJoinTasks.add(ForkJoinTask.adapt(task)); + invoke(new InvokeAll<T>(forkJoinTasks)); + + @SuppressWarnings({"unchecked", "rawtypes"}) + List<Future<T>> futures = (List<Future<T>>) (List) forkJoinTasks; + return futures; + } + + static final class InvokeAll<T> extends RecursiveAction { + final ArrayList<ForkJoinTask<T>> tasks; + InvokeAll(ArrayList<ForkJoinTask<T>> tasks) { this.tasks = tasks; } + public void compute() { + try { invokeAll(tasks); } + catch (Exception ignore) {} + } + private static final long serialVersionUID = -7914297376763021607L; + } + + /** + * Returns the factory used for constructing new workers. + * + * @return the factory used for constructing new workers + */ + public ForkJoinWorkerThreadFactory getFactory() { + return factory; + } + + /** + * Returns the handler for internal worker threads that terminate + * due to unrecoverable errors encountered while executing tasks. + * + * @return the handler, or {@code null} if none + */ + public Thread.UncaughtExceptionHandler getUncaughtExceptionHandler() { + return ueh; + } + + /** + * Returns the targeted parallelism level of this pool. + * + * @return the targeted parallelism level of this pool + */ + public int getParallelism() { + return parallelism; + } + + /** + * Returns the number of worker threads that have started but not + * yet terminated. The result returned by this method may differ + * from {@link #getParallelism} when threads are created to + * maintain parallelism when others are cooperatively blocked. + * + * @return the number of worker threads + */ + public int getPoolSize() { + return parallelism + (short)(ctl >>> TC_SHIFT); + } + + /** + * Returns {@code true} if this pool uses local first-in-first-out + * scheduling mode for forked tasks that are never joined. + * + * @return {@code true} if this pool uses async mode + */ + public boolean getAsyncMode() { + return locallyFifo; + } + + /** + * Returns an estimate of the number of worker threads that are + * not blocked waiting to join tasks or for other managed + * synchronization. This method may overestimate the + * number of running threads. + * + * @return the number of worker threads + */ + public int getRunningThreadCount() { + int r = parallelism + (int)(ctl >> AC_SHIFT); + return (r <= 0) ? 0 : r; // suppress momentarily negative values + } + + /** + * Returns an estimate of the number of threads that are currently + * stealing or executing tasks. This method may overestimate the + * number of active threads. + * + * @return the number of active threads + */ + public int getActiveThreadCount() { + int r = parallelism + (int)(ctl >> AC_SHIFT) + blockedCount; + return (r <= 0) ? 0 : r; // suppress momentarily negative values + } + + /** + * Returns {@code true} if all worker threads are currently idle. + * An idle worker is one that cannot obtain a task to execute + * because none are available to steal from other threads, and + * there are no pending submissions to the pool. This method is + * conservative; it might not return {@code true} immediately upon + * idleness of all threads, but will eventually become true if + * threads remain inactive. + * + * @return {@code true} if all threads are currently idle + */ + public boolean isQuiescent() { + return parallelism + (int)(ctl >> AC_SHIFT) + blockedCount == 0; + } + + /** + * Returns an estimate of the total number of tasks stolen from + * one thread's work queue by another. The reported value + * underestimates the actual total number of steals when the pool + * is not quiescent. This value may be useful for monitoring and + * tuning fork/join programs: in general, steal counts should be + * high enough to keep threads busy, but low enough to avoid + * overhead and contention across threads. + * + * @return the number of steals + */ + public long getStealCount() { + return stealCount; + } + + /** + * Returns an estimate of the total number of tasks currently held + * in queues by worker threads (but not including tasks submitted + * to the pool that have not begun executing). This value is only + * an approximation, obtained by iterating across all threads in + * the pool. This method may be useful for tuning task + * granularities. + * + * @return the number of queued tasks + */ + public long getQueuedTaskCount() { + long count = 0; + ForkJoinWorkerThread[] ws; + if ((short)(ctl >>> TC_SHIFT) > -parallelism && + (ws = workers) != null) { + for (ForkJoinWorkerThread w : ws) + if (w != null) + count -= w.queueBase - w.queueTop; // must read base first + } + return count; + } + + /** + * Returns an estimate of the number of tasks submitted to this + * pool that have not yet begun executing. This method may take + * time proportional to the number of submissions. + * + * @return the number of queued submissions + */ + public int getQueuedSubmissionCount() { + return -queueBase + queueTop; + } + + /** + * Returns {@code true} if there are any tasks submitted to this + * pool that have not yet begun executing. + * + * @return {@code true} if there are any queued submissions + */ + public boolean hasQueuedSubmissions() { + return queueBase != queueTop; + } + + /** + * Removes and returns the next unexecuted submission if one is + * available. This method may be useful in extensions to this + * class that re-assign work in systems with multiple pools. + * + * @return the next submission, or {@code null} if none + */ + protected ForkJoinTask<?> pollSubmission() { + ForkJoinTask<?> t; ForkJoinTask<?>[] q; int b, i; + while ((b = queueBase) != queueTop && + (q = submissionQueue) != null && + (i = (q.length - 1) & b) >= 0) { + long u = (i << ASHIFT) + ABASE; + if ((t = q[i]) != null && + queueBase == b && + UNSAFE.compareAndSwapObject(q, u, t, null)) { + queueBase = b + 1; + return t; + } + } + return null; + } + + /** + * Removes all available unexecuted submitted and forked tasks + * from scheduling queues and adds them to the given collection, + * without altering their execution status. These may include + * artificially generated or wrapped tasks. This method is + * designed to be invoked only when the pool is known to be + * quiescent. Invocations at other times may not remove all + * tasks. A failure encountered while attempting to add elements + * to collection {@code c} may result in elements being in + * neither, either or both collections when the associated + * exception is thrown. The behavior of this operation is + * undefined if the specified collection is modified while the + * operation is in progress. + * + * @param c the collection to transfer elements into + * @return the number of elements transferred + */ + protected int drainTasksTo(Collection<? super ForkJoinTask<?>> c) { + int count = 0; + while (queueBase != queueTop) { + ForkJoinTask<?> t = pollSubmission(); + if (t != null) { + c.add(t); + ++count; + } + } + ForkJoinWorkerThread[] ws; + if ((short)(ctl >>> TC_SHIFT) > -parallelism && + (ws = workers) != null) { + for (ForkJoinWorkerThread w : ws) + if (w != null) + count += w.drainTasksTo(c); + } + return count; + } + + /** + * Returns a string identifying this pool, as well as its state, + * including indications of run state, parallelism level, and + * worker and task counts. + * + * @return a string identifying this pool, as well as its state + */ + public String toString() { + long st = getStealCount(); + long qt = getQueuedTaskCount(); + long qs = getQueuedSubmissionCount(); + int pc = parallelism; + long c = ctl; + int tc = pc + (short)(c >>> TC_SHIFT); + int rc = pc + (int)(c >> AC_SHIFT); + if (rc < 0) // ignore transient negative + rc = 0; + int ac = rc + blockedCount; + String level; + if ((c & STOP_BIT) != 0) + level = (tc == 0) ? "Terminated" : "Terminating"; + else + level = shutdown ? "Shutting down" : "Running"; + return super.toString() + + "[" + level + + ", parallelism = " + pc + + ", size = " + tc + + ", active = " + ac + + ", running = " + rc + + ", steals = " + st + + ", tasks = " + qt + + ", submissions = " + qs + + "]"; + } + + /** + * Initiates an orderly shutdown in which previously submitted + * tasks are executed, but no new tasks will be accepted. + * Invocation has no additional effect if already shut down. + * Tasks that are in the process of being submitted concurrently + * during the course of this method may or may not be rejected. + */ + public void shutdown() { + checkPermission(); + shutdown = true; + tryTerminate(false); + } + + /** + * Attempts to cancel and/or stop all tasks, and reject all + * subsequently submitted tasks. Tasks that are in the process of + * being submitted or executed concurrently during the course of + * this method may or may not be rejected. This method cancels + * both existing and unexecuted tasks, in order to permit + * termination in the presence of task dependencies. So the method + * always returns an empty list (unlike the case for some other + * Executors). + * + * @return an empty list + */ + public List<Runnable> shutdownNow() { + checkPermission(); + shutdown = true; + tryTerminate(true); + return Collections.emptyList(); + } + + /** + * Returns {@code true} if all tasks have completed following shut down. + * + * @return {@code true} if all tasks have completed following shut down + */ + public boolean isTerminated() { + long c = ctl; + return ((c & STOP_BIT) != 0L && + (short)(c >>> TC_SHIFT) == -parallelism); + } + + /** + * Returns {@code true} if the process of termination has + * commenced but not yet completed. This method may be useful for + * debugging. A return of {@code true} reported a sufficient + * period after shutdown may indicate that submitted tasks have + * ignored or suppressed interruption, or are waiting for IO, + * causing this executor not to properly terminate. (See the + * advisory notes for class {@link ForkJoinTask} stating that + * tasks should not normally entail blocking operations. But if + * they do, they must abort them on interrupt.) + * + * @return {@code true} if terminating but not yet terminated + */ + public boolean isTerminating() { + long c = ctl; + return ((c & STOP_BIT) != 0L && + (short)(c >>> TC_SHIFT) != -parallelism); + } + + /** + * Returns true if terminating or terminated. Used by ForkJoinWorkerThread. + */ + final boolean isAtLeastTerminating() { + return (ctl & STOP_BIT) != 0L; + } + + /** + * Returns {@code true} if this pool has been shut down. + * + * @return {@code true} if this pool has been shut down + */ + public boolean isShutdown() { + return shutdown; + } + + /** + * Blocks until all tasks have completed execution after a shutdown + * request, or the timeout occurs, or the current thread is + * interrupted, whichever happens first. + * + * @param timeout the maximum time to wait + * @param unit the time unit of the timeout argument + * @return {@code true} if this executor terminated and + * {@code false} if the timeout elapsed before termination + * @throws InterruptedException if interrupted while waiting + */ + public boolean awaitTermination(long timeout, TimeUnit unit) + throws InterruptedException { + long nanos = unit.toNanos(timeout); + final ReentrantLock lock = this.submissionLock; + lock.lock(); + try { + for (;;) { + if (isTerminated()) + return true; + if (nanos <= 0) + return false; + nanos = termination.awaitNanos(nanos); + } + } finally { + lock.unlock(); + } + } + + /** + * Interface for extending managed parallelism for tasks running + * in {@link ForkJoinPool}s. + * + * <p>A {@code ManagedBlocker} provides two methods. Method + * {@code isReleasable} must return {@code true} if blocking is + * not necessary. Method {@code block} blocks the current thread + * if necessary (perhaps internally invoking {@code isReleasable} + * before actually blocking). These actions are performed by any + * thread invoking {@link ForkJoinPool#managedBlock}. The + * unusual methods in this API accommodate synchronizers that may, + * but don't usually, block for long periods. Similarly, they + * allow more efficient internal handling of cases in which + * additional workers may be, but usually are not, needed to + * ensure sufficient parallelism. Toward this end, + * implementations of method {@code isReleasable} must be amenable + * to repeated invocation. + * + * <p>For example, here is a ManagedBlocker based on a + * ReentrantLock: + * <pre> {@code + * class ManagedLocker implements ManagedBlocker { + * final ReentrantLock lock; + * boolean hasLock = false; + * ManagedLocker(ReentrantLock lock) { this.lock = lock; } + * public boolean block() { + * if (!hasLock) + * lock.lock(); + * return true; + * } + * public boolean isReleasable() { + * return hasLock || (hasLock = lock.tryLock()); + * } + * }}</pre> + * + * <p>Here is a class that possibly blocks waiting for an + * item on a given queue: + * <pre> {@code + * class QueueTaker<E> implements ManagedBlocker { + * final BlockingQueue<E> queue; + * volatile E item = null; + * QueueTaker(BlockingQueue<E> q) { this.queue = q; } + * public boolean block() throws InterruptedException { + * if (item == null) + * item = queue.take(); + * return true; + * } + * public boolean isReleasable() { + * return item != null || (item = queue.poll()) != null; + * } + * public E getItem() { // call after pool.managedBlock completes + * return item; + * } + * }}</pre> + */ + public static interface ManagedBlocker { + /** + * Possibly blocks the current thread, for example waiting for + * a lock or condition. + * + * @return {@code true} if no additional blocking is necessary + * (i.e., if isReleasable would return true) + * @throws InterruptedException if interrupted while waiting + * (the method is not required to do so, but is allowed to) + */ + boolean block() throws InterruptedException; + + /** + * Returns {@code true} if blocking is unnecessary. + */ + boolean isReleasable(); + } + + /** + * Blocks in accord with the given blocker. If the current thread + * is a {@link ForkJoinWorkerThread}, this method possibly + * arranges for a spare thread to be activated if necessary to + * ensure sufficient parallelism while the current thread is blocked. + * + * <p>If the caller is not a {@link ForkJoinTask}, this method is + * behaviorally equivalent to + * <pre> {@code + * while (!blocker.isReleasable()) + * if (blocker.block()) + * return; + * }</pre> + * + * If the caller is a {@code ForkJoinTask}, then the pool may + * first be expanded to ensure parallelism, and later adjusted. + * + * @param blocker the blocker + * @throws InterruptedException if blocker.block did so + */ + public static void managedBlock(ManagedBlocker blocker) + throws InterruptedException { + Thread t = Thread.currentThread(); + if (t instanceof ForkJoinWorkerThread) { + ForkJoinWorkerThread w = (ForkJoinWorkerThread) t; + w.pool.awaitBlocker(blocker); + } + else { + do {} while (!blocker.isReleasable() && !blocker.block()); + } + } + + // AbstractExecutorService overrides. These rely on undocumented + // fact that ForkJoinTask.adapt returns ForkJoinTasks that also + // implement RunnableFuture. + + protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) { + return (RunnableFuture<T>) ForkJoinTask.adapt(runnable, value); + } + + protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) { + return (RunnableFuture<T>) ForkJoinTask.adapt(callable); + } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE; + private static final long ctlOffset; + private static final long stealCountOffset; + private static final long blockedCountOffset; + private static final long quiescerCountOffset; + private static final long scanGuardOffset; + private static final long nextWorkerNumberOffset; + private static final long ABASE; + private static final int ASHIFT; + + static { + poolNumberGenerator = new AtomicInteger(); + workerSeedGenerator = new Random(); + modifyThreadPermission = new RuntimePermission("modifyThread"); + defaultForkJoinWorkerThreadFactory = + new DefaultForkJoinWorkerThreadFactory(); + try { + UNSAFE = sun.misc.Unsafe.getUnsafe(); + Class<?> k = ForkJoinPool.class; + ctlOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("ctl")); + stealCountOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("stealCount")); + blockedCountOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("blockedCount")); + quiescerCountOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("quiescerCount")); + scanGuardOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("scanGuard")); + nextWorkerNumberOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("nextWorkerNumber")); + } catch (Exception e) { + throw new Error(e); + } + Class<?> a = ForkJoinTask[].class; + ABASE = UNSAFE.arrayBaseOffset(a); + int s = UNSAFE.arrayIndexScale(a); + if ((s & (s-1)) != 0) + throw new Error("data type scale not a power of two"); + ASHIFT = 31 - Integer.numberOfLeadingZeros(s); + } + +} diff --git a/luni/src/main/java/java/util/concurrent/ForkJoinTask.java b/luni/src/main/java/java/util/concurrent/ForkJoinTask.java new file mode 100644 index 0000000..86a29d7 --- /dev/null +++ b/luni/src/main/java/java/util/concurrent/ForkJoinTask.java @@ -0,0 +1,1357 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +package java.util.concurrent; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import java.util.RandomAccess; +import java.lang.ref.WeakReference; +import java.lang.ref.ReferenceQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.RunnableFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.ReentrantLock; +import java.lang.reflect.Constructor; +import libcore.util.SneakyThrow; + +/** + * Abstract base class for tasks that run within a {@link ForkJoinPool}. + * A {@code ForkJoinTask} is a thread-like entity that is much + * lighter weight than a normal thread. Huge numbers of tasks and + * subtasks may be hosted by a small number of actual threads in a + * ForkJoinPool, at the price of some usage limitations. + * + * <p>A "main" {@code ForkJoinTask} begins execution when submitted + * to a {@link ForkJoinPool}. Once started, it will usually in turn + * start other subtasks. As indicated by the name of this class, + * many programs using {@code ForkJoinTask} employ only methods + * {@link #fork} and {@link #join}, or derivatives such as {@link + * #invokeAll(ForkJoinTask...) invokeAll}. However, this class also + * provides a number of other methods that can come into play in + * advanced usages, as well as extension mechanics that allow + * support of new forms of fork/join processing. + * + * <p>A {@code ForkJoinTask} is a lightweight form of {@link Future}. + * The efficiency of {@code ForkJoinTask}s stems from a set of + * restrictions (that are only partially statically enforceable) + * reflecting their intended use as computational tasks calculating + * pure functions or operating on purely isolated objects. The + * primary coordination mechanisms are {@link #fork}, that arranges + * asynchronous execution, and {@link #join}, that doesn't proceed + * until the task's result has been computed. Computations should + * avoid {@code synchronized} methods or blocks, and should minimize + * other blocking synchronization apart from joining other tasks or + * using synchronizers such as Phasers that are advertised to + * cooperate with fork/join scheduling. Tasks should also not perform + * blocking IO, and should ideally access variables that are + * completely independent of those accessed by other running + * tasks. Minor breaches of these restrictions, for example using + * shared output streams, may be tolerable in practice, but frequent + * use may result in poor performance, and the potential to + * indefinitely stall if the number of threads not waiting for IO or + * other external synchronization becomes exhausted. This usage + * restriction is in part enforced by not permitting checked + * exceptions such as {@code IOExceptions} to be thrown. However, + * computations may still encounter unchecked exceptions, that are + * rethrown to callers attempting to join them. These exceptions may + * additionally include {@link RejectedExecutionException} stemming + * from internal resource exhaustion, such as failure to allocate + * internal task queues. Rethrown exceptions behave in the same way as + * regular exceptions, but, when possible, contain stack traces (as + * displayed for example using {@code ex.printStackTrace()}) of both + * the thread that initiated the computation as well as the thread + * actually encountering the exception; minimally only the latter. + * + * <p>The primary method for awaiting completion and extracting + * results of a task is {@link #join}, but there are several variants: + * The {@link Future#get} methods support interruptible and/or timed + * waits for completion and report results using {@code Future} + * conventions. Method {@link #invoke} is semantically + * equivalent to {@code fork(); join()} but always attempts to begin + * execution in the current thread. The "<em>quiet</em>" forms of + * these methods do not extract results or report exceptions. These + * may be useful when a set of tasks are being executed, and you need + * to delay processing of results or exceptions until all complete. + * Method {@code invokeAll} (available in multiple versions) + * performs the most common form of parallel invocation: forking a set + * of tasks and joining them all. + * + * <p>The execution status of tasks may be queried at several levels + * of detail: {@link #isDone} is true if a task completed in any way + * (including the case where a task was cancelled without executing); + * {@link #isCompletedNormally} is true if a task completed without + * cancellation or encountering an exception; {@link #isCancelled} is + * true if the task was cancelled (in which case {@link #getException} + * returns a {@link java.util.concurrent.CancellationException}); and + * {@link #isCompletedAbnormally} is true if a task was either + * cancelled or encountered an exception, in which case {@link + * #getException} will return either the encountered exception or + * {@link java.util.concurrent.CancellationException}. + * + * <p>The ForkJoinTask class is not usually directly subclassed. + * Instead, you subclass one of the abstract classes that support a + * particular style of fork/join processing, typically {@link + * RecursiveAction} for computations that do not return results, or + * {@link RecursiveTask} for those that do. Normally, a concrete + * ForkJoinTask subclass declares fields comprising its parameters, + * established in a constructor, and then defines a {@code compute} + * method that somehow uses the control methods supplied by this base + * class. While these methods have {@code public} access (to allow + * instances of different task subclasses to call each other's + * methods), some of them may only be called from within other + * ForkJoinTasks (as may be determined using method {@link + * #inForkJoinPool}). Attempts to invoke them in other contexts + * result in exceptions or errors, possibly including + * {@code ClassCastException}. + * + * <p>Method {@link #join} and its variants are appropriate for use + * only when completion dependencies are acyclic; that is, the + * parallel computation can be described as a directed acyclic graph + * (DAG). Otherwise, executions may encounter a form of deadlock as + * tasks cyclically wait for each other. However, this framework + * supports other methods and techniques (for example the use of + * {@link Phaser}, {@link #helpQuiesce}, and {@link #complete}) that + * may be of use in constructing custom subclasses for problems that + * are not statically structured as DAGs. + * + * <p>Most base support methods are {@code final}, to prevent + * overriding of implementations that are intrinsically tied to the + * underlying lightweight task scheduling framework. Developers + * creating new basic styles of fork/join processing should minimally + * implement {@code protected} methods {@link #exec}, {@link + * #setRawResult}, and {@link #getRawResult}, while also introducing + * an abstract computational method that can be implemented in its + * subclasses, possibly relying on other {@code protected} methods + * provided by this class. + * + * <p>ForkJoinTasks should perform relatively small amounts of + * computation. Large tasks should be split into smaller subtasks, + * usually via recursive decomposition. As a very rough rule of thumb, + * a task should perform more than 100 and less than 10000 basic + * computational steps, and should avoid indefinite looping. If tasks + * are too big, then parallelism cannot improve throughput. If too + * small, then memory and internal task maintenance overhead may + * overwhelm processing. + * + * <p>This class provides {@code adapt} methods for {@link Runnable} + * and {@link Callable}, that may be of use when mixing execution of + * {@code ForkJoinTasks} with other kinds of tasks. When all tasks are + * of this form, consider using a pool constructed in <em>asyncMode</em>. + * + * <p>ForkJoinTasks are {@code Serializable}, which enables them to be + * used in extensions such as remote execution frameworks. It is + * sensible to serialize tasks only before or after, but not during, + * execution. Serialization is not relied on during execution itself. + * + * @since 1.7 + * @hide + * @author Doug Lea + */ +public abstract class ForkJoinTask<V> implements Future<V>, Serializable { + + /* + * See the internal documentation of class ForkJoinPool for a + * general implementation overview. ForkJoinTasks are mainly + * responsible for maintaining their "status" field amidst relays + * to methods in ForkJoinWorkerThread and ForkJoinPool. + * + * The methods of this class are more-or-less layered into + * (1) basic status maintenance + * (2) execution and awaiting completion + * (3) user-level methods that additionally report results. + * This is sometimes hard to see because this file orders exported + * methods in a way that flows well in javadocs. + */ + + /* + * The status field holds run control status bits packed into a + * single int to minimize footprint and to ensure atomicity (via + * CAS). Status is initially zero, and takes on nonnegative + * values until completed, upon which status holds value + * NORMAL, CANCELLED, or EXCEPTIONAL. Tasks undergoing blocking + * waits by other threads have the SIGNAL bit set. Completion of + * a stolen task with SIGNAL set awakens any waiters via + * notifyAll. Even though suboptimal for some purposes, we use + * basic builtin wait/notify to take advantage of "monitor + * inflation" in JVMs that we would otherwise need to emulate to + * avoid adding further per-task bookkeeping overhead. We want + * these monitors to be "fat", i.e., not use biasing or thin-lock + * techniques, so use some odd coding idioms that tend to avoid + * them. + */ + + /** The run status of this task */ + volatile int status; // accessed directly by pool and workers + private static final int NORMAL = -1; + private static final int CANCELLED = -2; + private static final int EXCEPTIONAL = -3; + private static final int SIGNAL = 1; + + /** + * Marks completion and wakes up threads waiting to join this task, + * also clearing signal request bits. + * + * @param completion one of NORMAL, CANCELLED, EXCEPTIONAL + * @return completion status on exit + */ + private int setCompletion(int completion) { + for (int s;;) { + if ((s = status) < 0) + return s; + if (UNSAFE.compareAndSwapInt(this, statusOffset, s, completion)) { + if (s != 0) + synchronized (this) { notifyAll(); } + return completion; + } + } + } + + /** + * Tries to block a worker thread until completed or timed out. + * Uses Object.wait time argument conventions. + * May fail on contention or interrupt. + * + * @param millis if > 0, wait time. + */ + final void tryAwaitDone(long millis) { + int s; + try { + if (((s = status) > 0 || + (s == 0 && + UNSAFE.compareAndSwapInt(this, statusOffset, 0, SIGNAL))) && + status > 0) { + synchronized (this) { + if (status > 0) + wait(millis); + } + } + } catch (InterruptedException ie) { + // caller must check termination + } + } + + /** + * Blocks a non-worker-thread until completion. + * @return status upon completion + */ + private int externalAwaitDone() { + int s; + if ((s = status) >= 0) { + boolean interrupted = false; + synchronized (this) { + while ((s = status) >= 0) { + if (s == 0) + UNSAFE.compareAndSwapInt(this, statusOffset, + 0, SIGNAL); + else { + try { + wait(); + } catch (InterruptedException ie) { + interrupted = true; + } + } + } + } + if (interrupted) + Thread.currentThread().interrupt(); + } + return s; + } + + /** + * Blocks a non-worker-thread until completion or interruption or timeout. + */ + private int externalInterruptibleAwaitDone(long millis) + throws InterruptedException { + int s; + if (Thread.interrupted()) + throw new InterruptedException(); + if ((s = status) >= 0) { + synchronized (this) { + while ((s = status) >= 0) { + if (s == 0) + UNSAFE.compareAndSwapInt(this, statusOffset, + 0, SIGNAL); + else { + wait(millis); + if (millis > 0L) + break; + } + } + } + } + return s; + } + + /** + * Primary execution method for stolen tasks. Unless done, calls + * exec and records status if completed, but doesn't wait for + * completion otherwise. + */ + final void doExec() { + if (status >= 0) { + boolean completed; + try { + completed = exec(); + } catch (Throwable rex) { + setExceptionalCompletion(rex); + return; + } + if (completed) + setCompletion(NORMAL); // must be outside try block + } + } + + /** + * Primary mechanics for join, get, quietlyJoin. + * @return status upon completion + */ + private int doJoin() { + Thread t; ForkJoinWorkerThread w; int s; boolean completed; + if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) { + if ((s = status) < 0) + return s; + if ((w = (ForkJoinWorkerThread)t).unpushTask(this)) { + try { + completed = exec(); + } catch (Throwable rex) { + return setExceptionalCompletion(rex); + } + if (completed) + return setCompletion(NORMAL); + } + return w.joinTask(this); + } + else + return externalAwaitDone(); + } + + /** + * Primary mechanics for invoke, quietlyInvoke. + * @return status upon completion + */ + private int doInvoke() { + int s; boolean completed; + if ((s = status) < 0) + return s; + try { + completed = exec(); + } catch (Throwable rex) { + return setExceptionalCompletion(rex); + } + if (completed) + return setCompletion(NORMAL); + else + return doJoin(); + } + + // Exception table support + + /** + * Table of exceptions thrown by tasks, to enable reporting by + * callers. Because exceptions are rare, we don't directly keep + * them with task objects, but instead use a weak ref table. Note + * that cancellation exceptions don't appear in the table, but are + * instead recorded as status values. + * + * Note: These statics are initialized below in static block. + */ + private static final ExceptionNode[] exceptionTable; + private static final ReentrantLock exceptionTableLock; + private static final ReferenceQueue<Object> exceptionTableRefQueue; + + /** + * Fixed capacity for exceptionTable. + */ + private static final int EXCEPTION_MAP_CAPACITY = 32; + + /** + * Key-value nodes for exception table. The chained hash table + * uses identity comparisons, full locking, and weak references + * for keys. The table has a fixed capacity because it only + * maintains task exceptions long enough for joiners to access + * them, so should never become very large for sustained + * periods. However, since we do not know when the last joiner + * completes, we must use weak references and expunge them. We do + * so on each operation (hence full locking). Also, some thread in + * any ForkJoinPool will call helpExpungeStaleExceptions when its + * pool becomes isQuiescent. + */ + static final class ExceptionNode extends WeakReference<ForkJoinTask<?>>{ + final Throwable ex; + ExceptionNode next; + final long thrower; // use id not ref to avoid weak cycles + ExceptionNode(ForkJoinTask<?> task, Throwable ex, ExceptionNode next) { + super(task, exceptionTableRefQueue); + this.ex = ex; + this.next = next; + this.thrower = Thread.currentThread().getId(); + } + } + + /** + * Records exception and sets exceptional completion. + * + * @return status on exit + */ + private int setExceptionalCompletion(Throwable ex) { + int h = System.identityHashCode(this); + final ReentrantLock lock = exceptionTableLock; + lock.lock(); + try { + expungeStaleExceptions(); + ExceptionNode[] t = exceptionTable; + int i = h & (t.length - 1); + for (ExceptionNode e = t[i]; ; e = e.next) { + if (e == null) { + t[i] = new ExceptionNode(this, ex, t[i]); + break; + } + if (e.get() == this) // already present + break; + } + } finally { + lock.unlock(); + } + return setCompletion(EXCEPTIONAL); + } + + /** + * Removes exception node and clears status + */ + private void clearExceptionalCompletion() { + int h = System.identityHashCode(this); + final ReentrantLock lock = exceptionTableLock; + lock.lock(); + try { + ExceptionNode[] t = exceptionTable; + int i = h & (t.length - 1); + ExceptionNode e = t[i]; + ExceptionNode pred = null; + while (e != null) { + ExceptionNode next = e.next; + if (e.get() == this) { + if (pred == null) + t[i] = next; + else + pred.next = next; + break; + } + pred = e; + e = next; + } + expungeStaleExceptions(); + status = 0; + } finally { + lock.unlock(); + } + } + + /** + * Returns a rethrowable exception for the given task, if + * available. To provide accurate stack traces, if the exception + * was not thrown by the current thread, we try to create a new + * exception of the same type as the one thrown, but with the + * recorded exception as its cause. If there is no such + * constructor, we instead try to use a no-arg constructor, + * followed by initCause, to the same effect. If none of these + * apply, or any fail due to other exceptions, we return the + * recorded exception, which is still correct, although it may + * contain a misleading stack trace. + * + * @return the exception, or null if none + */ + private Throwable getThrowableException() { + if (status != EXCEPTIONAL) + return null; + int h = System.identityHashCode(this); + ExceptionNode e; + final ReentrantLock lock = exceptionTableLock; + lock.lock(); + try { + expungeStaleExceptions(); + ExceptionNode[] t = exceptionTable; + e = t[h & (t.length - 1)]; + while (e != null && e.get() != this) + e = e.next; + } finally { + lock.unlock(); + } + Throwable ex; + if (e == null || (ex = e.ex) == null) + return null; + if (e.thrower != Thread.currentThread().getId()) { + Class<? extends Throwable> ec = ex.getClass(); + try { + Constructor<?> noArgCtor = null; + Constructor<?>[] cs = ec.getConstructors();// public ctors only + for (int i = 0; i < cs.length; ++i) { + Constructor<?> c = cs[i]; + Class<?>[] ps = c.getParameterTypes(); + if (ps.length == 0) + noArgCtor = c; + else if (ps.length == 1 && ps[0] == Throwable.class) + return (Throwable)(c.newInstance(ex)); + } + if (noArgCtor != null) { + Throwable wx = (Throwable)(noArgCtor.newInstance()); + wx.initCause(ex); + return wx; + } + } catch (Exception ignore) { + } + } + return ex; + } + + /** + * Poll stale refs and remove them. Call only while holding lock. + */ + private static void expungeStaleExceptions() { + for (Object x; (x = exceptionTableRefQueue.poll()) != null;) { + if (x instanceof ExceptionNode) { + ForkJoinTask<?> key = ((ExceptionNode)x).get(); + ExceptionNode[] t = exceptionTable; + int i = System.identityHashCode(key) & (t.length - 1); + ExceptionNode e = t[i]; + ExceptionNode pred = null; + while (e != null) { + ExceptionNode next = e.next; + if (e == x) { + if (pred == null) + t[i] = next; + else + pred.next = next; + break; + } + pred = e; + e = next; + } + } + } + } + + /** + * If lock is available, poll stale refs and remove them. + * Called from ForkJoinPool when pools become quiescent. + */ + static final void helpExpungeStaleExceptions() { + final ReentrantLock lock = exceptionTableLock; + if (lock.tryLock()) { + try { + expungeStaleExceptions(); + } finally { + lock.unlock(); + } + } + } + + /** + * Report the result of invoke or join; called only upon + * non-normal return of internal versions. + */ + private V reportResult() { + int s; Throwable ex; + if ((s = status) == CANCELLED) + throw new CancellationException(); + if (s == EXCEPTIONAL && (ex = getThrowableException()) != null) + SneakyThrow.sneakyThrow(ex); // android-changed + return getRawResult(); + } + + // public methods + + /** + * Arranges to asynchronously execute this task. While it is not + * necessarily enforced, it is a usage error to fork a task more + * than once unless it has completed and been reinitialized. + * Subsequent modifications to the state of this task or any data + * it operates on are not necessarily consistently observable by + * any thread other than the one executing it unless preceded by a + * call to {@link #join} or related methods, or a call to {@link + * #isDone} returning {@code true}. + * + * <p>This method may be invoked only from within {@code + * ForkJoinPool} computations (as may be determined using method + * {@link #inForkJoinPool}). Attempts to invoke in other contexts + * result in exceptions or errors, possibly including {@code + * ClassCastException}. + * + * @return {@code this}, to simplify usage + */ + public final ForkJoinTask<V> fork() { + ((ForkJoinWorkerThread) Thread.currentThread()) + .pushTask(this); + return this; + } + + /** + * Returns the result of the computation when it {@link #isDone is + * done}. This method differs from {@link #get()} in that + * abnormal completion results in {@code RuntimeException} or + * {@code Error}, not {@code ExecutionException}, and that + * interrupts of the calling thread do <em>not</em> cause the + * method to abruptly return by throwing {@code + * InterruptedException}. + * + * @return the computed result + */ + public final V join() { + if (doJoin() != NORMAL) + return reportResult(); + else + return getRawResult(); + } + + /** + * Commences performing this task, awaits its completion if + * necessary, and returns its result, or throws an (unchecked) + * {@code RuntimeException} or {@code Error} if the underlying + * computation did so. + * + * @return the computed result + */ + public final V invoke() { + if (doInvoke() != NORMAL) + return reportResult(); + else + return getRawResult(); + } + + /** + * Forks the given tasks, returning when {@code isDone} holds for + * each task or an (unchecked) exception is encountered, in which + * case the exception is rethrown. If more than one task + * encounters an exception, then this method throws any one of + * these exceptions. If any task encounters an exception, the + * other may be cancelled. However, the execution status of + * individual tasks is not guaranteed upon exceptional return. The + * status of each task may be obtained using {@link + * #getException()} and related methods to check if they have been + * cancelled, completed normally or exceptionally, or left + * unprocessed. + * + * <p>This method may be invoked only from within {@code + * ForkJoinPool} computations (as may be determined using method + * {@link #inForkJoinPool}). Attempts to invoke in other contexts + * result in exceptions or errors, possibly including {@code + * ClassCastException}. + * + * @param t1 the first task + * @param t2 the second task + * @throws NullPointerException if any task is null + */ + public static void invokeAll(ForkJoinTask<?> t1, ForkJoinTask<?> t2) { + t2.fork(); + t1.invoke(); + t2.join(); + } + + /** + * Forks the given tasks, returning when {@code isDone} holds for + * each task or an (unchecked) exception is encountered, in which + * case the exception is rethrown. If more than one task + * encounters an exception, then this method throws any one of + * these exceptions. If any task encounters an exception, others + * may be cancelled. However, the execution status of individual + * tasks is not guaranteed upon exceptional return. The status of + * each task may be obtained using {@link #getException()} and + * related methods to check if they have been cancelled, completed + * normally or exceptionally, or left unprocessed. + * + * <p>This method may be invoked only from within {@code + * ForkJoinPool} computations (as may be determined using method + * {@link #inForkJoinPool}). Attempts to invoke in other contexts + * result in exceptions or errors, possibly including {@code + * ClassCastException}. + * + * @param tasks the tasks + * @throws NullPointerException if any task is null + */ + public static void invokeAll(ForkJoinTask<?>... tasks) { + Throwable ex = null; + int last = tasks.length - 1; + for (int i = last; i >= 0; --i) { + ForkJoinTask<?> t = tasks[i]; + if (t == null) { + if (ex == null) + ex = new NullPointerException(); + } + else if (i != 0) + t.fork(); + else if (t.doInvoke() < NORMAL && ex == null) + ex = t.getException(); + } + for (int i = 1; i <= last; ++i) { + ForkJoinTask<?> t = tasks[i]; + if (t != null) { + if (ex != null) + t.cancel(false); + else if (t.doJoin() < NORMAL) + ex = t.getException(); + } + } + if (ex != null) + SneakyThrow.sneakyThrow(ex); // android-changed + } + + /** + * Forks all tasks in the specified collection, returning when + * {@code isDone} holds for each task or an (unchecked) exception + * is encountered, in which case the exception is rethrown. If + * more than one task encounters an exception, then this method + * throws any one of these exceptions. If any task encounters an + * exception, others may be cancelled. However, the execution + * status of individual tasks is not guaranteed upon exceptional + * return. The status of each task may be obtained using {@link + * #getException()} and related methods to check if they have been + * cancelled, completed normally or exceptionally, or left + * unprocessed. + * + * <p>This method may be invoked only from within {@code + * ForkJoinPool} computations (as may be determined using method + * {@link #inForkJoinPool}). Attempts to invoke in other contexts + * result in exceptions or errors, possibly including {@code + * ClassCastException}. + * + * @param tasks the collection of tasks + * @return the tasks argument, to simplify usage + * @throws NullPointerException if tasks or any element are null + */ + public static <T extends ForkJoinTask<?>> Collection<T> invokeAll(Collection<T> tasks) { + if (!(tasks instanceof RandomAccess) || !(tasks instanceof List<?>)) { + invokeAll(tasks.toArray(new ForkJoinTask<?>[tasks.size()])); + return tasks; + } + @SuppressWarnings("unchecked") + List<? extends ForkJoinTask<?>> ts = + (List<? extends ForkJoinTask<?>>) tasks; + Throwable ex = null; + int last = ts.size() - 1; + for (int i = last; i >= 0; --i) { + ForkJoinTask<?> t = ts.get(i); + if (t == null) { + if (ex == null) + ex = new NullPointerException(); + } + else if (i != 0) + t.fork(); + else if (t.doInvoke() < NORMAL && ex == null) + ex = t.getException(); + } + for (int i = 1; i <= last; ++i) { + ForkJoinTask<?> t = ts.get(i); + if (t != null) { + if (ex != null) + t.cancel(false); + else if (t.doJoin() < NORMAL) + ex = t.getException(); + } + } + if (ex != null) + SneakyThrow.sneakyThrow(ex); // android-changed + return tasks; + } + + /** + * Attempts to cancel execution of this task. This attempt will + * fail if the task has already completed or could not be + * cancelled for some other reason. If successful, and this task + * has not started when {@code cancel} is called, execution of + * this task is suppressed. After this method returns + * successfully, unless there is an intervening call to {@link + * #reinitialize}, subsequent calls to {@link #isCancelled}, + * {@link #isDone}, and {@code cancel} will return {@code true} + * and calls to {@link #join} and related methods will result in + * {@code CancellationException}. + * + * <p>This method may be overridden in subclasses, but if so, must + * still ensure that these properties hold. In particular, the + * {@code cancel} method itself must not throw exceptions. + * + * <p>This method is designed to be invoked by <em>other</em> + * tasks. To terminate the current task, you can just return or + * throw an unchecked exception from its computation method, or + * invoke {@link #completeExceptionally}. + * + * @param mayInterruptIfRunning this value has no effect in the + * default implementation because interrupts are not used to + * control cancellation. + * + * @return {@code true} if this task is now cancelled + */ + public boolean cancel(boolean mayInterruptIfRunning) { + return setCompletion(CANCELLED) == CANCELLED; + } + + /** + * Cancels, ignoring any exceptions thrown by cancel. Used during + * worker and pool shutdown. Cancel is spec'ed not to throw any + * exceptions, but if it does anyway, we have no recourse during + * shutdown, so guard against this case. + */ + final void cancelIgnoringExceptions() { + try { + cancel(false); + } catch (Throwable ignore) { + } + } + + public final boolean isDone() { + return status < 0; + } + + public final boolean isCancelled() { + return status == CANCELLED; + } + + /** + * Returns {@code true} if this task threw an exception or was cancelled. + * + * @return {@code true} if this task threw an exception or was cancelled + */ + public final boolean isCompletedAbnormally() { + return status < NORMAL; + } + + /** + * Returns {@code true} if this task completed without throwing an + * exception and was not cancelled. + * + * @return {@code true} if this task completed without throwing an + * exception and was not cancelled + */ + public final boolean isCompletedNormally() { + return status == NORMAL; + } + + /** + * Returns the exception thrown by the base computation, or a + * {@code CancellationException} if cancelled, or {@code null} if + * none or if the method has not yet completed. + * + * @return the exception, or {@code null} if none + */ + public final Throwable getException() { + int s = status; + return ((s >= NORMAL) ? null : + (s == CANCELLED) ? new CancellationException() : + getThrowableException()); + } + + /** + * Completes this task abnormally, and if not already aborted or + * cancelled, causes it to throw the given exception upon + * {@code join} and related operations. This method may be used + * to induce exceptions in asynchronous tasks, or to force + * completion of tasks that would not otherwise complete. Its use + * in other situations is discouraged. This method is + * overridable, but overridden versions must invoke {@code super} + * implementation to maintain guarantees. + * + * @param ex the exception to throw. If this exception is not a + * {@code RuntimeException} or {@code Error}, the actual exception + * thrown will be a {@code RuntimeException} with cause {@code ex}. + */ + public void completeExceptionally(Throwable ex) { + setExceptionalCompletion((ex instanceof RuntimeException) || + (ex instanceof Error) ? ex : + new RuntimeException(ex)); + } + + /** + * Completes this task, and if not already aborted or cancelled, + * returning the given value as the result of subsequent + * invocations of {@code join} and related operations. This method + * may be used to provide results for asynchronous tasks, or to + * provide alternative handling for tasks that would not otherwise + * complete normally. Its use in other situations is + * discouraged. This method is overridable, but overridden + * versions must invoke {@code super} implementation to maintain + * guarantees. + * + * @param value the result value for this task + */ + public void complete(V value) { + try { + setRawResult(value); + } catch (Throwable rex) { + setExceptionalCompletion(rex); + return; + } + setCompletion(NORMAL); + } + + /** + * Waits if necessary for the computation to complete, and then + * retrieves its result. + * + * @return the computed result + * @throws CancellationException if the computation was cancelled + * @throws ExecutionException if the computation threw an + * exception + * @throws InterruptedException if the current thread is not a + * member of a ForkJoinPool and was interrupted while waiting + */ + public final V get() throws InterruptedException, ExecutionException { + int s = (Thread.currentThread() instanceof ForkJoinWorkerThread) ? + doJoin() : externalInterruptibleAwaitDone(0L); + Throwable ex; + if (s == CANCELLED) + throw new CancellationException(); + if (s == EXCEPTIONAL && (ex = getThrowableException()) != null) + throw new ExecutionException(ex); + return getRawResult(); + } + + /** + * Waits if necessary for at most the given time for the computation + * to complete, and then retrieves its result, if available. + * + * @param timeout the maximum time to wait + * @param unit the time unit of the timeout argument + * @return the computed result + * @throws CancellationException if the computation was cancelled + * @throws ExecutionException if the computation threw an + * exception + * @throws InterruptedException if the current thread is not a + * member of a ForkJoinPool and was interrupted while waiting + * @throws TimeoutException if the wait timed out + */ + public final V get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + Thread t = Thread.currentThread(); + if (t instanceof ForkJoinWorkerThread) { + ForkJoinWorkerThread w = (ForkJoinWorkerThread) t; + long nanos = unit.toNanos(timeout); + if (status >= 0) { + boolean completed = false; + if (w.unpushTask(this)) { + try { + completed = exec(); + } catch (Throwable rex) { + setExceptionalCompletion(rex); + } + } + if (completed) + setCompletion(NORMAL); + else if (status >= 0 && nanos > 0) + w.pool.timedAwaitJoin(this, nanos); + } + } + else { + long millis = unit.toMillis(timeout); + if (millis > 0) + externalInterruptibleAwaitDone(millis); + } + int s = status; + if (s != NORMAL) { + Throwable ex; + if (s == CANCELLED) + throw new CancellationException(); + if (s != EXCEPTIONAL) + throw new TimeoutException(); + if ((ex = getThrowableException()) != null) + throw new ExecutionException(ex); + } + return getRawResult(); + } + + /** + * Joins this task, without returning its result or throwing its + * exception. This method may be useful when processing + * collections of tasks when some have been cancelled or otherwise + * known to have aborted. + */ + public final void quietlyJoin() { + doJoin(); + } + + /** + * Commences performing this task and awaits its completion if + * necessary, without returning its result or throwing its + * exception. + */ + public final void quietlyInvoke() { + doInvoke(); + } + + /** + * Possibly executes tasks until the pool hosting the current task + * {@link ForkJoinPool#isQuiescent is quiescent}. This method may + * be of use in designs in which many tasks are forked, but none + * are explicitly joined, instead executing them until all are + * processed. + * + * <p>This method may be invoked only from within {@code + * ForkJoinPool} computations (as may be determined using method + * {@link #inForkJoinPool}). Attempts to invoke in other contexts + * result in exceptions or errors, possibly including {@code + * ClassCastException}. + */ + public static void helpQuiesce() { + ((ForkJoinWorkerThread) Thread.currentThread()) + .helpQuiescePool(); + } + + /** + * Resets the internal bookkeeping state of this task, allowing a + * subsequent {@code fork}. This method allows repeated reuse of + * this task, but only if reuse occurs when this task has either + * never been forked, or has been forked, then completed and all + * outstanding joins of this task have also completed. Effects + * under any other usage conditions are not guaranteed. + * This method may be useful when executing + * pre-constructed trees of subtasks in loops. + * + * <p>Upon completion of this method, {@code isDone()} reports + * {@code false}, and {@code getException()} reports {@code + * null}. However, the value returned by {@code getRawResult} is + * unaffected. To clear this value, you can invoke {@code + * setRawResult(null)}. + */ + public void reinitialize() { + if (status == EXCEPTIONAL) + clearExceptionalCompletion(); + else + status = 0; + } + + /** + * Returns the pool hosting the current task execution, or null + * if this task is executing outside of any ForkJoinPool. + * + * @see #inForkJoinPool + * @return the pool, or {@code null} if none + */ + public static ForkJoinPool getPool() { + Thread t = Thread.currentThread(); + return (t instanceof ForkJoinWorkerThread) ? + ((ForkJoinWorkerThread) t).pool : null; + } + + /** + * Returns {@code true} if the current thread is a {@link + * ForkJoinWorkerThread} executing as a ForkJoinPool computation. + * + * @return {@code true} if the current thread is a {@link + * ForkJoinWorkerThread} executing as a ForkJoinPool computation, + * or {@code false} otherwise + */ + public static boolean inForkJoinPool() { + return Thread.currentThread() instanceof ForkJoinWorkerThread; + } + + /** + * Tries to unschedule this task for execution. This method will + * typically succeed if this task is the most recently forked task + * by the current thread, and has not commenced executing in + * another thread. This method may be useful when arranging + * alternative local processing of tasks that could have been, but + * were not, stolen. + * + * <p>This method may be invoked only from within {@code + * ForkJoinPool} computations (as may be determined using method + * {@link #inForkJoinPool}). Attempts to invoke in other contexts + * result in exceptions or errors, possibly including {@code + * ClassCastException}. + * + * @return {@code true} if unforked + */ + public boolean tryUnfork() { + return ((ForkJoinWorkerThread) Thread.currentThread()) + .unpushTask(this); + } + + /** + * Returns an estimate of the number of tasks that have been + * forked by the current worker thread but not yet executed. This + * value may be useful for heuristic decisions about whether to + * fork other tasks. + * + * <p>This method may be invoked only from within {@code + * ForkJoinPool} computations (as may be determined using method + * {@link #inForkJoinPool}). Attempts to invoke in other contexts + * result in exceptions or errors, possibly including {@code + * ClassCastException}. + * + * @return the number of tasks + */ + public static int getQueuedTaskCount() { + return ((ForkJoinWorkerThread) Thread.currentThread()) + .getQueueSize(); + } + + /** + * Returns an estimate of how many more locally queued tasks are + * held by the current worker thread than there are other worker + * threads that might steal them. This value may be useful for + * heuristic decisions about whether to fork other tasks. In many + * usages of ForkJoinTasks, at steady state, each worker should + * aim to maintain a small constant surplus (for example, 3) of + * tasks, and to process computations locally if this threshold is + * exceeded. + * + * <p>This method may be invoked only from within {@code + * ForkJoinPool} computations (as may be determined using method + * {@link #inForkJoinPool}). Attempts to invoke in other contexts + * result in exceptions or errors, possibly including {@code + * ClassCastException}. + * + * @return the surplus number of tasks, which may be negative + */ + public static int getSurplusQueuedTaskCount() { + return ((ForkJoinWorkerThread) Thread.currentThread()) + .getEstimatedSurplusTaskCount(); + } + + // Extension methods + + /** + * Returns the result that would be returned by {@link #join}, even + * if this task completed abnormally, or {@code null} if this task + * is not known to have been completed. This method is designed + * to aid debugging, as well as to support extensions. Its use in + * any other context is discouraged. + * + * @return the result, or {@code null} if not completed + */ + public abstract V getRawResult(); + + /** + * Forces the given value to be returned as a result. This method + * is designed to support extensions, and should not in general be + * called otherwise. + * + * @param value the value + */ + protected abstract void setRawResult(V value); + + /** + * Immediately performs the base action of this task. This method + * is designed to support extensions, and should not in general be + * called otherwise. The return value controls whether this task + * is considered to be done normally. It may return false in + * asynchronous actions that require explicit invocations of + * {@link #complete} to become joinable. It may also throw an + * (unchecked) exception to indicate abnormal exit. + * + * @return {@code true} if completed normally + */ + protected abstract boolean exec(); + + /** + * Returns, but does not unschedule or execute, a task queued by + * the current thread but not yet executed, if one is immediately + * available. There is no guarantee that this task will actually + * be polled or executed next. Conversely, this method may return + * null even if a task exists but cannot be accessed without + * contention with other threads. This method is designed + * primarily to support extensions, and is unlikely to be useful + * otherwise. + * + * <p>This method may be invoked only from within {@code + * ForkJoinPool} computations (as may be determined using method + * {@link #inForkJoinPool}). Attempts to invoke in other contexts + * result in exceptions or errors, possibly including {@code + * ClassCastException}. + * + * @return the next task, or {@code null} if none are available + */ + protected static ForkJoinTask<?> peekNextLocalTask() { + return ((ForkJoinWorkerThread) Thread.currentThread()) + .peekTask(); + } + + /** + * Unschedules and returns, without executing, the next task + * queued by the current thread but not yet executed. This method + * is designed primarily to support extensions, and is unlikely to + * be useful otherwise. + * + * <p>This method may be invoked only from within {@code + * ForkJoinPool} computations (as may be determined using method + * {@link #inForkJoinPool}). Attempts to invoke in other contexts + * result in exceptions or errors, possibly including {@code + * ClassCastException}. + * + * @return the next task, or {@code null} if none are available + */ + protected static ForkJoinTask<?> pollNextLocalTask() { + return ((ForkJoinWorkerThread) Thread.currentThread()) + .pollLocalTask(); + } + + /** + * Unschedules and returns, without executing, the next task + * queued by the current thread but not yet executed, if one is + * available, or if not available, a task that was forked by some + * other thread, if available. Availability may be transient, so a + * {@code null} result does not necessarily imply quiescence + * of the pool this task is operating in. This method is designed + * primarily to support extensions, and is unlikely to be useful + * otherwise. + * + * <p>This method may be invoked only from within {@code + * ForkJoinPool} computations (as may be determined using method + * {@link #inForkJoinPool}). Attempts to invoke in other contexts + * result in exceptions or errors, possibly including {@code + * ClassCastException}. + * + * @return a task, or {@code null} if none are available + */ + protected static ForkJoinTask<?> pollTask() { + return ((ForkJoinWorkerThread) Thread.currentThread()) + .pollTask(); + } + + /** + * Adaptor for Runnables. This implements RunnableFuture + * to be compliant with AbstractExecutorService constraints + * when used in ForkJoinPool. + */ + static final class AdaptedRunnable<T> extends ForkJoinTask<T> + implements RunnableFuture<T> { + final Runnable runnable; + final T resultOnCompletion; + T result; + AdaptedRunnable(Runnable runnable, T result) { + if (runnable == null) throw new NullPointerException(); + this.runnable = runnable; + this.resultOnCompletion = result; + } + public T getRawResult() { return result; } + public void setRawResult(T v) { result = v; } + public boolean exec() { + runnable.run(); + result = resultOnCompletion; + return true; + } + public void run() { invoke(); } + private static final long serialVersionUID = 5232453952276885070L; + } + + /** + * Adaptor for Callables + */ + static final class AdaptedCallable<T> extends ForkJoinTask<T> + implements RunnableFuture<T> { + final Callable<? extends T> callable; + T result; + AdaptedCallable(Callable<? extends T> callable) { + if (callable == null) throw new NullPointerException(); + this.callable = callable; + } + public T getRawResult() { return result; } + public void setRawResult(T v) { result = v; } + public boolean exec() { + try { + result = callable.call(); + return true; + } catch (Error err) { + throw err; + } catch (RuntimeException rex) { + throw rex; + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + public void run() { invoke(); } + private static final long serialVersionUID = 2838392045355241008L; + } + + /** + * Returns a new {@code ForkJoinTask} that performs the {@code run} + * method of the given {@code Runnable} as its action, and returns + * a null result upon {@link #join}. + * + * @param runnable the runnable action + * @return the task + */ + public static ForkJoinTask<?> adapt(Runnable runnable) { + return new AdaptedRunnable<Void>(runnable, null); + } + + /** + * Returns a new {@code ForkJoinTask} that performs the {@code run} + * method of the given {@code Runnable} as its action, and returns + * the given result upon {@link #join}. + * + * @param runnable the runnable action + * @param result the result upon completion + * @return the task + */ + public static <T> ForkJoinTask<T> adapt(Runnable runnable, T result) { + return new AdaptedRunnable<T>(runnable, result); + } + + /** + * Returns a new {@code ForkJoinTask} that performs the {@code call} + * method of the given {@code Callable} as its action, and returns + * its result upon {@link #join}, translating any checked exceptions + * encountered into {@code RuntimeException}. + * + * @param callable the callable action + * @return the task + */ + public static <T> ForkJoinTask<T> adapt(Callable<? extends T> callable) { + return new AdaptedCallable<T>(callable); + } + + // Serialization support + + private static final long serialVersionUID = -7721805057305804111L; + + /** + * Saves the state to a stream (that is, serializes it). + * + * @serialData the current run status and the exception thrown + * during execution, or {@code null} if none + * @param s the stream + */ + private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException { + s.defaultWriteObject(); + s.writeObject(getException()); + } + + /** + * Reconstitutes the instance from a stream (that is, deserializes it). + * + * @param s the stream + */ + private void readObject(java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + s.defaultReadObject(); + Object ex = s.readObject(); + if (ex != null) + setExceptionalCompletion((Throwable)ex); + } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE; + private static final long statusOffset; + static { + exceptionTableLock = new ReentrantLock(); + exceptionTableRefQueue = new ReferenceQueue<Object>(); + exceptionTable = new ExceptionNode[EXCEPTION_MAP_CAPACITY]; + try { + UNSAFE = sun.misc.Unsafe.getUnsafe(); + statusOffset = UNSAFE.objectFieldOffset + (ForkJoinTask.class.getDeclaredField("status")); + } catch (Exception e) { + throw new Error(e); + } + } + +} diff --git a/luni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java b/luni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java new file mode 100644 index 0000000..d99ffe9 --- /dev/null +++ b/luni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java @@ -0,0 +1,970 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +package java.util.concurrent; + +import java.util.Collection; +import java.util.concurrent.RejectedExecutionException; +import libcore.util.SneakyThrow; + +/** + * A thread managed by a {@link ForkJoinPool}, which executes + * {@link ForkJoinTask}s. + * This class is subclassable solely for the sake of adding + * functionality -- there are no overridable methods dealing with + * scheduling or execution. However, you can override initialization + * and termination methods surrounding the main task processing loop. + * If you do create such a subclass, you will also need to supply a + * custom {@link ForkJoinPool.ForkJoinWorkerThreadFactory} to use it + * in a {@code ForkJoinPool}. + * + * @since 1.7 + * @hide + * @author Doug Lea + */ +public class ForkJoinWorkerThread extends Thread { + /* + * Overview: + * + * ForkJoinWorkerThreads are managed by ForkJoinPools and perform + * ForkJoinTasks. This class includes bookkeeping in support of + * worker activation, suspension, and lifecycle control described + * in more detail in the internal documentation of class + * ForkJoinPool. And as described further below, this class also + * includes special-cased support for some ForkJoinTask + * methods. But the main mechanics involve work-stealing: + * + * Work-stealing queues are special forms of Deques that support + * only three of the four possible end-operations -- push, pop, + * and deq (aka steal), under the further constraints that push + * and pop are called only from the owning thread, while deq may + * be called from other threads. (If you are unfamiliar with + * them, you probably want to read Herlihy and Shavit's book "The + * Art of Multiprocessor programming", chapter 16 describing these + * in more detail before proceeding.) The main work-stealing + * queue design is roughly similar to those in the papers "Dynamic + * Circular Work-Stealing Deque" by Chase and Lev, SPAA 2005 + * (http://research.sun.com/scalable/pubs/index.html) and + * "Idempotent work stealing" by Michael, Saraswat, and Vechev, + * PPoPP 2009 (http://portal.acm.org/citation.cfm?id=1504186). + * The main differences ultimately stem from gc requirements that + * we null out taken slots as soon as we can, to maintain as small + * a footprint as possible even in programs generating huge + * numbers of tasks. To accomplish this, we shift the CAS + * arbitrating pop vs deq (steal) from being on the indices + * ("queueBase" and "queueTop") to the slots themselves (mainly + * via method "casSlotNull()"). So, both a successful pop and deq + * mainly entail a CAS of a slot from non-null to null. Because + * we rely on CASes of references, we do not need tag bits on + * queueBase or queueTop. They are simple ints as used in any + * circular array-based queue (see for example ArrayDeque). + * Updates to the indices must still be ordered in a way that + * guarantees that queueTop == queueBase means the queue is empty, + * but otherwise may err on the side of possibly making the queue + * appear nonempty when a push, pop, or deq have not fully + * committed. Note that this means that the deq operation, + * considered individually, is not wait-free. One thief cannot + * successfully continue until another in-progress one (or, if + * previously empty, a push) completes. However, in the + * aggregate, we ensure at least probabilistic non-blockingness. + * If an attempted steal fails, a thief always chooses a different + * random victim target to try next. So, in order for one thief to + * progress, it suffices for any in-progress deq or new push on + * any empty queue to complete. + * + * This approach also enables support for "async mode" where local + * task processing is in FIFO, not LIFO order; simply by using a + * version of deq rather than pop when locallyFifo is true (as set + * by the ForkJoinPool). This allows use in message-passing + * frameworks in which tasks are never joined. However neither + * mode considers affinities, loads, cache localities, etc, so + * rarely provide the best possible performance on a given + * machine, but portably provide good throughput by averaging over + * these factors. (Further, even if we did try to use such + * information, we do not usually have a basis for exploiting + * it. For example, some sets of tasks profit from cache + * affinities, but others are harmed by cache pollution effects.) + * + * When a worker would otherwise be blocked waiting to join a + * task, it first tries a form of linear helping: Each worker + * records (in field currentSteal) the most recent task it stole + * from some other worker. Plus, it records (in field currentJoin) + * the task it is currently actively joining. Method joinTask uses + * these markers to try to find a worker to help (i.e., steal back + * a task from and execute it) that could hasten completion of the + * actively joined task. In essence, the joiner executes a task + * that would be on its own local deque had the to-be-joined task + * not been stolen. This may be seen as a conservative variant of + * the approach in Wagner & Calder "Leapfrogging: a portable + * technique for implementing efficient futures" SIGPLAN Notices, + * 1993 (http://portal.acm.org/citation.cfm?id=155354). It differs + * in that: (1) We only maintain dependency links across workers + * upon steals, rather than use per-task bookkeeping. This may + * require a linear scan of workers array to locate stealers, but + * usually doesn't because stealers leave hints (that may become + * stale/wrong) of where to locate them. This isolates cost to + * when it is needed, rather than adding to per-task overhead. + * (2) It is "shallow", ignoring nesting and potentially cyclic + * mutual steals. (3) It is intentionally racy: field currentJoin + * is updated only while actively joining, which means that we + * miss links in the chain during long-lived tasks, GC stalls etc + * (which is OK since blocking in such cases is usually a good + * idea). (4) We bound the number of attempts to find work (see + * MAX_HELP) and fall back to suspending the worker and if + * necessary replacing it with another. + * + * Efficient implementation of these algorithms currently relies + * on an uncomfortable amount of "Unsafe" mechanics. To maintain + * correct orderings, reads and writes of variable queueBase + * require volatile ordering. Variable queueTop need not be + * volatile because non-local reads always follow those of + * queueBase. Similarly, because they are protected by volatile + * queueBase reads, reads of the queue array and its slots by + * other threads do not need volatile load semantics, but writes + * (in push) require store order and CASes (in pop and deq) + * require (volatile) CAS semantics. (Michael, Saraswat, and + * Vechev's algorithm has similar properties, but without support + * for nulling slots.) Since these combinations aren't supported + * using ordinary volatiles, the only way to accomplish these + * efficiently is to use direct Unsafe calls. (Using external + * AtomicIntegers and AtomicReferenceArrays for the indices and + * array is significantly slower because of memory locality and + * indirection effects.) + * + * Further, performance on most platforms is very sensitive to + * placement and sizing of the (resizable) queue array. Even + * though these queues don't usually become all that big, the + * initial size must be large enough to counteract cache + * contention effects across multiple queues (especially in the + * presence of GC cardmarking). Also, to improve thread-locality, + * queues are initialized after starting. + */ + + /** + * Mask for pool indices encoded as shorts + */ + private static final int SMASK = 0xffff; + + /** + * Capacity of work-stealing queue array upon initialization. + * Must be a power of two. Initial size must be at least 4, but is + * padded to minimize cache effects. + */ + private static final int INITIAL_QUEUE_CAPACITY = 1 << 13; + + /** + * Maximum size for queue array. Must be a power of two + * less than or equal to 1 << (31 - width of array entry) to + * ensure lack of index wraparound, but is capped at a lower + * value to help users trap runaway computations. + */ + private static final int MAXIMUM_QUEUE_CAPACITY = 1 << 24; // 16M + + /** + * The work-stealing queue array. Size must be a power of two. + * Initialized when started (as opposed to when constructed), to + * improve memory locality. + */ + ForkJoinTask<?>[] queue; + + /** + * The pool this thread works in. Accessed directly by ForkJoinTask. + */ + final ForkJoinPool pool; + + /** + * Index (mod queue.length) of next queue slot to push to or pop + * from. It is written only by owner thread, and accessed by other + * threads only after reading (volatile) queueBase. Both queueTop + * and queueBase are allowed to wrap around on overflow, but + * (queueTop - queueBase) still estimates size. + */ + int queueTop; + + /** + * Index (mod queue.length) of least valid queue slot, which is + * always the next position to steal from if nonempty. + */ + volatile int queueBase; + + /** + * The index of most recent stealer, used as a hint to avoid + * traversal in method helpJoinTask. This is only a hint because a + * worker might have had multiple steals and this only holds one + * of them (usually the most current). Declared non-volatile, + * relying on other prevailing sync to keep reasonably current. + */ + int stealHint; + + /** + * Index of this worker in pool array. Set once by pool before + * running, and accessed directly by pool to locate this worker in + * its workers array. + */ + final int poolIndex; + + /** + * Encoded record for pool task waits. Usages are always + * surrounded by volatile reads/writes + */ + int nextWait; + + /** + * Complement of poolIndex, offset by count of entries of task + * waits. Accessed by ForkJoinPool to manage event waiters. + */ + volatile int eventCount; + + /** + * Seed for random number generator for choosing steal victims. + * Uses Marsaglia xorshift. Must be initialized as nonzero. + */ + int seed; + + /** + * Number of steals. Directly accessed (and reset) by pool when + * idle. + */ + int stealCount; + + /** + * True if this worker should or did terminate + */ + volatile boolean terminate; + + /** + * Set to true before LockSupport.park; false on return + */ + volatile boolean parked; + + /** + * True if use local fifo, not default lifo, for local polling. + * Shadows value from ForkJoinPool. + */ + final boolean locallyFifo; + + /** + * The task most recently stolen from another worker (or + * submission queue). All uses are surrounded by enough volatile + * reads/writes to maintain as non-volatile. + */ + ForkJoinTask<?> currentSteal; + + /** + * The task currently being joined, set only when actively trying + * to help other stealers in helpJoinTask. All uses are surrounded + * by enough volatile reads/writes to maintain as non-volatile. + */ + ForkJoinTask<?> currentJoin; + + /** + * Creates a ForkJoinWorkerThread operating in the given pool. + * + * @param pool the pool this thread works in + * @throws NullPointerException if pool is null + */ + protected ForkJoinWorkerThread(ForkJoinPool pool) { + super(pool.nextWorkerName()); + this.pool = pool; + int k = pool.registerWorker(this); + poolIndex = k; + eventCount = ~k & SMASK; // clear wait count + locallyFifo = pool.locallyFifo; + Thread.UncaughtExceptionHandler ueh = pool.ueh; + if (ueh != null) + setUncaughtExceptionHandler(ueh); + setDaemon(true); + } + + // Public methods + + /** + * Returns the pool hosting this thread. + * + * @return the pool + */ + public ForkJoinPool getPool() { + return pool; + } + + /** + * Returns the index number of this thread in its pool. The + * returned value ranges from zero to the maximum number of + * threads (minus one) that have ever been created in the pool. + * This method may be useful for applications that track status or + * collect results per-worker rather than per-task. + * + * @return the index number + */ + public int getPoolIndex() { + return poolIndex; + } + + // Randomization + + /** + * Computes next value for random victim probes and backoffs. + * Scans don't require a very high quality generator, but also not + * a crummy one. Marsaglia xor-shift is cheap and works well + * enough. Note: This is manually inlined in FJP.scan() to avoid + * writes inside busy loops. + */ + private int nextSeed() { + int r = seed; + r ^= r << 13; + r ^= r >>> 17; + r ^= r << 5; + return seed = r; + } + + // Run State management + + /** + * Initializes internal state after construction but before + * processing any tasks. If you override this method, you must + * invoke {@code super.onStart()} at the beginning of the method. + * Initialization requires care: Most fields must have legal + * default values, to ensure that attempted accesses from other + * threads work correctly even before this thread starts + * processing tasks. + */ + protected void onStart() { + queue = new ForkJoinTask<?>[INITIAL_QUEUE_CAPACITY]; + int r = ForkJoinPool.workerSeedGenerator.nextInt(); + seed = (r == 0) ? 1 : r; // must be nonzero + } + + /** + * Performs cleanup associated with termination of this worker + * thread. If you override this method, you must invoke + * {@code super.onTermination} at the end of the overridden method. + * + * @param exception the exception causing this thread to abort due + * to an unrecoverable error, or {@code null} if completed normally + */ + protected void onTermination(Throwable exception) { + try { + terminate = true; + cancelTasks(); + pool.deregisterWorker(this, exception); + } catch (Throwable ex) { // Shouldn't ever happen + if (exception == null) // but if so, at least rethrown + exception = ex; + } finally { + if (exception != null) + SneakyThrow.sneakyThrow(exception); // android-changed + } + } + + /** + * This method is required to be public, but should never be + * called explicitly. It performs the main run loop to execute + * {@link ForkJoinTask}s. + */ + public void run() { + Throwable exception = null; + try { + onStart(); + pool.work(this); + } catch (Throwable ex) { + exception = ex; + } finally { + onTermination(exception); + } + } + + /* + * Intrinsics-based atomic writes for queue slots. These are + * basically the same as methods in AtomicReferenceArray, but + * specialized for (1) ForkJoinTask elements (2) requirement that + * nullness and bounds checks have already been performed by + * callers and (3) effective offsets are known not to overflow + * from int to long (because of MAXIMUM_QUEUE_CAPACITY). We don't + * need corresponding version for reads: plain array reads are OK + * because they are protected by other volatile reads and are + * confirmed by CASes. + * + * Most uses don't actually call these methods, but instead + * contain inlined forms that enable more predictable + * optimization. We don't define the version of write used in + * pushTask at all, but instead inline there a store-fenced array + * slot write. + * + * Also in most methods, as a performance (not correctness) issue, + * we'd like to encourage compilers not to arbitrarily postpone + * setting queueTop after writing slot. Currently there is no + * intrinsic for arranging this, but using Unsafe putOrderedInt + * may be a preferable strategy on some compilers even though its + * main effect is a pre-, not post- fence. To simplify possible + * changes, the option is left in comments next to the associated + * assignments. + */ + + /** + * CASes slot i of array q from t to null. Caller must ensure q is + * non-null and index is in range. + */ + private static final boolean casSlotNull(ForkJoinTask<?>[] q, int i, + ForkJoinTask<?> t) { + return UNSAFE.compareAndSwapObject(q, (i << ASHIFT) + ABASE, t, null); + } + + /** + * Performs a volatile write of the given task at given slot of + * array q. Caller must ensure q is non-null and index is in + * range. This method is used only during resets and backouts. + */ + private static final void writeSlot(ForkJoinTask<?>[] q, int i, + ForkJoinTask<?> t) { + UNSAFE.putObjectVolatile(q, (i << ASHIFT) + ABASE, t); + } + + // queue methods + + /** + * Pushes a task. Call only from this thread. + * + * @param t the task. Caller must ensure non-null. + */ + final void pushTask(ForkJoinTask<?> t) { + ForkJoinTask<?>[] q; int s, m; + if ((q = queue) != null) { // ignore if queue removed + long u = (((s = queueTop) & (m = q.length - 1)) << ASHIFT) + ABASE; + UNSAFE.putOrderedObject(q, u, t); + queueTop = s + 1; // or use putOrderedInt + if ((s -= queueBase) <= 2) + pool.signalWork(); + else if (s == m) + growQueue(); + } + } + + /** + * Creates or doubles queue array. Transfers elements by + * emulating steals (deqs) from old array and placing, oldest + * first, into new array. + */ + private void growQueue() { + ForkJoinTask<?>[] oldQ = queue; + int size = oldQ != null ? oldQ.length << 1 : INITIAL_QUEUE_CAPACITY; + if (size > MAXIMUM_QUEUE_CAPACITY) + throw new RejectedExecutionException("Queue capacity exceeded"); + if (size < INITIAL_QUEUE_CAPACITY) + size = INITIAL_QUEUE_CAPACITY; + ForkJoinTask<?>[] q = queue = new ForkJoinTask<?>[size]; + int mask = size - 1; + int top = queueTop; + int oldMask; + if (oldQ != null && (oldMask = oldQ.length - 1) >= 0) { + for (int b = queueBase; b != top; ++b) { + long u = ((b & oldMask) << ASHIFT) + ABASE; + Object x = UNSAFE.getObjectVolatile(oldQ, u); + if (x != null && UNSAFE.compareAndSwapObject(oldQ, u, x, null)) + UNSAFE.putObjectVolatile + (q, ((b & mask) << ASHIFT) + ABASE, x); + } + } + } + + /** + * Tries to take a task from the base of the queue, failing if + * empty or contended. Note: Specializations of this code appear + * in locallyDeqTask and elsewhere. + * + * @return a task, or null if none or contended + */ + final ForkJoinTask<?> deqTask() { + ForkJoinTask<?> t; ForkJoinTask<?>[] q; int b, i; + if (queueTop != (b = queueBase) && + (q = queue) != null && // must read q after b + (i = (q.length - 1) & b) >= 0 && + (t = q[i]) != null && queueBase == b && + UNSAFE.compareAndSwapObject(q, (i << ASHIFT) + ABASE, t, null)) { + queueBase = b + 1; + return t; + } + return null; + } + + /** + * Tries to take a task from the base of own queue. Called only + * by this thread. + * + * @return a task, or null if none + */ + final ForkJoinTask<?> locallyDeqTask() { + ForkJoinTask<?> t; int m, b, i; + ForkJoinTask<?>[] q = queue; + if (q != null && (m = q.length - 1) >= 0) { + while (queueTop != (b = queueBase)) { + if ((t = q[i = m & b]) != null && + queueBase == b && + UNSAFE.compareAndSwapObject(q, (i << ASHIFT) + ABASE, + t, null)) { + queueBase = b + 1; + return t; + } + } + } + return null; + } + + /** + * Returns a popped task, or null if empty. + * Called only by this thread. + */ + private ForkJoinTask<?> popTask() { + int m; + ForkJoinTask<?>[] q = queue; + if (q != null && (m = q.length - 1) >= 0) { + for (int s; (s = queueTop) != queueBase;) { + int i = m & --s; + long u = (i << ASHIFT) + ABASE; // raw offset + ForkJoinTask<?> t = q[i]; + if (t == null) // lost to stealer + break; + if (UNSAFE.compareAndSwapObject(q, u, t, null)) { + queueTop = s; // or putOrderedInt + return t; + } + } + } + return null; + } + + /** + * Specialized version of popTask to pop only if topmost element + * is the given task. Called only by this thread. + * + * @param t the task. Caller must ensure non-null. + */ + final boolean unpushTask(ForkJoinTask<?> t) { + ForkJoinTask<?>[] q; + int s; + if ((q = queue) != null && (s = queueTop) != queueBase && + UNSAFE.compareAndSwapObject + (q, (((q.length - 1) & --s) << ASHIFT) + ABASE, t, null)) { + queueTop = s; // or putOrderedInt + return true; + } + return false; + } + + /** + * Returns next task, or null if empty or contended. + */ + final ForkJoinTask<?> peekTask() { + int m; + ForkJoinTask<?>[] q = queue; + if (q == null || (m = q.length - 1) < 0) + return null; + int i = locallyFifo ? queueBase : (queueTop - 1); + return q[i & m]; + } + + // Support methods for ForkJoinPool + + /** + * Runs the given task, plus any local tasks until queue is empty + */ + final void execTask(ForkJoinTask<?> t) { + currentSteal = t; + for (;;) { + if (t != null) + t.doExec(); + if (queueTop == queueBase) + break; + t = locallyFifo ? locallyDeqTask() : popTask(); + } + ++stealCount; + currentSteal = null; + } + + /** + * Removes and cancels all tasks in queue. Can be called from any + * thread. + */ + final void cancelTasks() { + ForkJoinTask<?> cj = currentJoin; // try to cancel ongoing tasks + if (cj != null && cj.status >= 0) + cj.cancelIgnoringExceptions(); + ForkJoinTask<?> cs = currentSteal; + if (cs != null && cs.status >= 0) + cs.cancelIgnoringExceptions(); + while (queueBase != queueTop) { + ForkJoinTask<?> t = deqTask(); + if (t != null) + t.cancelIgnoringExceptions(); + } + } + + /** + * Drains tasks to given collection c. + * + * @return the number of tasks drained + */ + final int drainTasksTo(Collection<? super ForkJoinTask<?>> c) { + int n = 0; + while (queueBase != queueTop) { + ForkJoinTask<?> t = deqTask(); + if (t != null) { + c.add(t); + ++n; + } + } + return n; + } + + // Support methods for ForkJoinTask + + /** + * Returns an estimate of the number of tasks in the queue. + */ + final int getQueueSize() { + return queueTop - queueBase; + } + + /** + * Gets and removes a local task. + * + * @return a task, if available + */ + final ForkJoinTask<?> pollLocalTask() { + return locallyFifo ? locallyDeqTask() : popTask(); + } + + /** + * Gets and removes a local or stolen task. + * + * @return a task, if available + */ + final ForkJoinTask<?> pollTask() { + ForkJoinWorkerThread[] ws; + ForkJoinTask<?> t = pollLocalTask(); + if (t != null || (ws = pool.workers) == null) + return t; + int n = ws.length; // cheap version of FJP.scan + int steps = n << 1; + int r = nextSeed(); + int i = 0; + while (i < steps) { + ForkJoinWorkerThread w = ws[(i++ + r) & (n - 1)]; + if (w != null && w.queueBase != w.queueTop && w.queue != null) { + if ((t = w.deqTask()) != null) + return t; + i = 0; + } + } + return null; + } + + /** + * The maximum stolen->joining link depth allowed in helpJoinTask, + * as well as the maximum number of retries (allowing on average + * one staleness retry per level) per attempt to instead try + * compensation. Depths for legitimate chains are unbounded, but + * we use a fixed constant to avoid (otherwise unchecked) cycles + * and bound staleness of traversal parameters at the expense of + * sometimes blocking when we could be helping. + */ + private static final int MAX_HELP = 16; + + /** + * Possibly runs some tasks and/or blocks, until joinMe is done. + * + * @param joinMe the task to join + * @return completion status on exit + */ + final int joinTask(ForkJoinTask<?> joinMe) { + ForkJoinTask<?> prevJoin = currentJoin; + currentJoin = joinMe; + for (int s, retries = MAX_HELP;;) { + if ((s = joinMe.status) < 0) { + currentJoin = prevJoin; + return s; + } + if (retries > 0) { + if (queueTop != queueBase) { + if (!localHelpJoinTask(joinMe)) + retries = 0; // cannot help + } + else if (retries == MAX_HELP >>> 1) { + --retries; // check uncommon case + if (tryDeqAndExec(joinMe) >= 0) + Thread.yield(); // for politeness + } + else + retries = helpJoinTask(joinMe) ? MAX_HELP : retries - 1; + } + else { + retries = MAX_HELP; // restart if not done + pool.tryAwaitJoin(joinMe); + } + } + } + + /** + * If present, pops and executes the given task, or any other + * cancelled task + * + * @return false if any other non-cancelled task exists in local queue + */ + private boolean localHelpJoinTask(ForkJoinTask<?> joinMe) { + int s, i; ForkJoinTask<?>[] q; ForkJoinTask<?> t; + if ((s = queueTop) != queueBase && (q = queue) != null && + (i = (q.length - 1) & --s) >= 0 && + (t = q[i]) != null) { + if (t != joinMe && t.status >= 0) + return false; + if (UNSAFE.compareAndSwapObject + (q, (i << ASHIFT) + ABASE, t, null)) { + queueTop = s; // or putOrderedInt + t.doExec(); + } + } + return true; + } + + /** + * Tries to locate and execute tasks for a stealer of the given + * task, or in turn one of its stealers, Traces + * currentSteal->currentJoin links looking for a thread working on + * a descendant of the given task and with a non-empty queue to + * steal back and execute tasks from. The implementation is very + * branchy to cope with potential inconsistencies or loops + * encountering chains that are stale, unknown, or of length + * greater than MAX_HELP links. All of these cases are dealt with + * by just retrying by caller. + * + * @param joinMe the task to join + * @return true if ran a task + */ + private boolean helpJoinTask(ForkJoinTask<?> joinMe) { + boolean helped = false; + int m = pool.scanGuard & SMASK; + ForkJoinWorkerThread[] ws = pool.workers; + if (ws != null && ws.length > m && joinMe.status >= 0) { + int levels = MAX_HELP; // remaining chain length + ForkJoinTask<?> task = joinMe; // base of chain + outer:for (ForkJoinWorkerThread thread = this;;) { + // Try to find v, the stealer of task, by first using hint + ForkJoinWorkerThread v = ws[thread.stealHint & m]; + if (v == null || v.currentSteal != task) { + for (int j = 0; ;) { // search array + if ((v = ws[j]) != null && v.currentSteal == task) { + thread.stealHint = j; + break; // save hint for next time + } + if (++j > m) + break outer; // can't find stealer + } + } + // Try to help v, using specialized form of deqTask + for (;;) { + ForkJoinTask<?>[] q; int b, i; + if (joinMe.status < 0) + break outer; + if ((b = v.queueBase) == v.queueTop || + (q = v.queue) == null || + (i = (q.length-1) & b) < 0) + break; // empty + long u = (i << ASHIFT) + ABASE; + ForkJoinTask<?> t = q[i]; + if (task.status < 0) + break outer; // stale + if (t != null && v.queueBase == b && + UNSAFE.compareAndSwapObject(q, u, t, null)) { + v.queueBase = b + 1; + v.stealHint = poolIndex; + ForkJoinTask<?> ps = currentSteal; + currentSteal = t; + t.doExec(); + currentSteal = ps; + helped = true; + } + } + // Try to descend to find v's stealer + ForkJoinTask<?> next = v.currentJoin; + if (--levels > 0 && task.status >= 0 && + next != null && next != task) { + task = next; + thread = v; + } + else + break; // max levels, stale, dead-end, or cyclic + } + } + return helped; + } + + /** + * Performs an uncommon case for joinTask: If task t is at base of + * some workers queue, steals and executes it. + * + * @param t the task + * @return t's status + */ + private int tryDeqAndExec(ForkJoinTask<?> t) { + int m = pool.scanGuard & SMASK; + ForkJoinWorkerThread[] ws = pool.workers; + if (ws != null && ws.length > m && t.status >= 0) { + for (int j = 0; j <= m; ++j) { + ForkJoinTask<?>[] q; int b, i; + ForkJoinWorkerThread v = ws[j]; + if (v != null && + (b = v.queueBase) != v.queueTop && + (q = v.queue) != null && + (i = (q.length - 1) & b) >= 0 && + q[i] == t) { + long u = (i << ASHIFT) + ABASE; + if (v.queueBase == b && + UNSAFE.compareAndSwapObject(q, u, t, null)) { + v.queueBase = b + 1; + v.stealHint = poolIndex; + ForkJoinTask<?> ps = currentSteal; + currentSteal = t; + t.doExec(); + currentSteal = ps; + } + break; + } + } + } + return t.status; + } + + /** + * Implements ForkJoinTask.getSurplusQueuedTaskCount(). Returns + * an estimate of the number of tasks, offset by a function of + * number of idle workers. + * + * This method provides a cheap heuristic guide for task + * partitioning when programmers, frameworks, tools, or languages + * have little or no idea about task granularity. In essence by + * offering this method, we ask users only about tradeoffs in + * overhead vs expected throughput and its variance, rather than + * how finely to partition tasks. + * + * In a steady state strict (tree-structured) computation, each + * thread makes available for stealing enough tasks for other + * threads to remain active. Inductively, if all threads play by + * the same rules, each thread should make available only a + * constant number of tasks. + * + * The minimum useful constant is just 1. But using a value of 1 + * would require immediate replenishment upon each steal to + * maintain enough tasks, which is infeasible. Further, + * partitionings/granularities of offered tasks should minimize + * steal rates, which in general means that threads nearer the top + * of computation tree should generate more than those nearer the + * bottom. In perfect steady state, each thread is at + * approximately the same level of computation tree. However, + * producing extra tasks amortizes the uncertainty of progress and + * diffusion assumptions. + * + * So, users will want to use values larger, but not much larger + * than 1 to both smooth over transient shortages and hedge + * against uneven progress; as traded off against the cost of + * extra task overhead. We leave the user to pick a threshold + * value to compare with the results of this call to guide + * decisions, but recommend values such as 3. + * + * When all threads are active, it is on average OK to estimate + * surplus strictly locally. In steady-state, if one thread is + * maintaining say 2 surplus tasks, then so are others. So we can + * just use estimated queue length (although note that (queueTop - + * queueBase) can be an overestimate because of stealers lagging + * increments of queueBase). However, this strategy alone leads + * to serious mis-estimates in some non-steady-state conditions + * (ramp-up, ramp-down, other stalls). We can detect many of these + * by further considering the number of "idle" threads, that are + * known to have zero queued tasks, so compensate by a factor of + * (#idle/#active) threads. + */ + final int getEstimatedSurplusTaskCount() { + return queueTop - queueBase - pool.idlePerActive(); + } + + /** + * Runs tasks until {@code pool.isQuiescent()}. We piggyback on + * pool's active count ctl maintenance, but rather than blocking + * when tasks cannot be found, we rescan until all others cannot + * find tasks either. The bracketing by pool quiescerCounts + * updates suppresses pool auto-shutdown mechanics that could + * otherwise prematurely terminate the pool because all threads + * appear to be inactive. + */ + final void helpQuiescePool() { + boolean active = true; + ForkJoinTask<?> ps = currentSteal; // to restore below + ForkJoinPool p = pool; + p.addQuiescerCount(1); + for (;;) { + ForkJoinWorkerThread[] ws = p.workers; + ForkJoinWorkerThread v = null; + int n; + if (queueTop != queueBase) + v = this; + else if (ws != null && (n = ws.length) > 1) { + ForkJoinWorkerThread w; + int r = nextSeed(); // cheap version of FJP.scan + int steps = n << 1; + for (int i = 0; i < steps; ++i) { + if ((w = ws[(i + r) & (n - 1)]) != null && + w.queueBase != w.queueTop) { + v = w; + break; + } + } + } + if (v != null) { + ForkJoinTask<?> t; + if (!active) { + active = true; + p.addActiveCount(1); + } + if ((t = (v != this) ? v.deqTask() : + locallyFifo ? locallyDeqTask() : popTask()) != null) { + currentSteal = t; + t.doExec(); + currentSteal = ps; + } + } + else { + if (active) { + active = false; + p.addActiveCount(-1); + } + if (p.isQuiescent()) { + p.addActiveCount(1); + p.addQuiescerCount(-1); + break; + } + } + } + } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE; + private static final long ABASE; + private static final int ASHIFT; + + static { + int s; + try { + UNSAFE = sun.misc.Unsafe.getUnsafe(); + Class<?> a = ForkJoinTask[].class; + ABASE = UNSAFE.arrayBaseOffset(a); + s = UNSAFE.arrayIndexScale(a); + } catch (Exception e) { + throw new Error(e); + } + if ((s & (s-1)) != 0) + throw new Error("data type scale not a power of two"); + ASHIFT = 31 - Integer.numberOfLeadingZeros(s); + } + +} diff --git a/luni/src/main/java/java/util/concurrent/Future.java b/luni/src/main/java/java/util/concurrent/Future.java index eb84796..6a37729 100644 --- a/luni/src/main/java/java/util/concurrent/Future.java +++ b/luni/src/main/java/java/util/concurrent/Future.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; diff --git a/luni/src/main/java/java/util/concurrent/FutureTask.java b/luni/src/main/java/java/util/concurrent/FutureTask.java index 2f2335e..d51881d 100644 --- a/luni/src/main/java/java/util/concurrent/FutureTask.java +++ b/luni/src/main/java/java/util/concurrent/FutureTask.java @@ -1,55 +1,116 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; -import java.util.concurrent.locks.*; +import java.util.concurrent.locks.LockSupport; /** * A cancellable asynchronous computation. This class provides a base * implementation of {@link Future}, with methods to start and cancel * a computation, query to see if the computation is complete, and * retrieve the result of the computation. The result can only be - * retrieved when the computation has completed; the <tt>get</tt> - * method will block if the computation has not yet completed. Once + * retrieved when the computation has completed; the {@code get} + * methods will block if the computation has not yet completed. Once * the computation has completed, the computation cannot be restarted - * or cancelled. + * or cancelled (unless the computation is invoked using + * {@link #runAndReset}). * - * <p>A <tt>FutureTask</tt> can be used to wrap a {@link Callable} or - * {@link java.lang.Runnable} object. Because <tt>FutureTask</tt> - * implements <tt>Runnable</tt>, a <tt>FutureTask</tt> can be - * submitted to an {@link Executor} for execution. + * <p>A {@code FutureTask} can be used to wrap a {@link Callable} or + * {@link Runnable} object. Because {@code FutureTask} implements + * {@code Runnable}, a {@code FutureTask} can be submitted to an + * {@link Executor} for execution. * * <p>In addition to serving as a standalone class, this class provides - * <tt>protected</tt> functionality that may be useful when creating + * {@code protected} functionality that may be useful when creating * customized task classes. * * @since 1.5 * @author Doug Lea - * @param <V> The result type returned by this FutureTask's <tt>get</tt> method + * @param <V> The result type returned by this FutureTask's {@code get} methods */ public class FutureTask<V> implements RunnableFuture<V> { - /** Synchronization control for FutureTask */ - private final Sync sync; + /* + * Revision notes: This differs from previous versions of this + * class that relied on AbstractQueuedSynchronizer, mainly to + * avoid surprising users about retaining interrupt status during + * cancellation races. Sync control in the current design relies + * on a "state" field updated via CAS to track completion, along + * with a simple Treiber stack to hold waiting threads. + * + * Style note: As usual, we bypass overhead of using + * AtomicXFieldUpdaters and instead directly use Unsafe intrinsics. + */ + + /** + * The run state of this task, initially NEW. The run state + * transitions to a terminal state only in methods set, + * setException, and cancel. During completion, state may take on + * transient values of COMPLETING (while outcome is being set) or + * INTERRUPTING (only while interrupting the runner to satisfy a + * cancel(true)). Transitions from these intermediate to final + * states use cheaper ordered/lazy writes because values are unique + * and cannot be further modified. + * + * Possible state transitions: + * NEW -> COMPLETING -> NORMAL + * NEW -> COMPLETING -> EXCEPTIONAL + * NEW -> CANCELLED + * NEW -> INTERRUPTING -> INTERRUPTED + */ + private volatile int state; + private static final int NEW = 0; + private static final int COMPLETING = 1; + private static final int NORMAL = 2; + private static final int EXCEPTIONAL = 3; + private static final int CANCELLED = 4; + private static final int INTERRUPTING = 5; + private static final int INTERRUPTED = 6; + + /** The underlying callable; nulled out after running */ + private Callable<V> callable; + /** The result to return or exception to throw from get() */ + private Object outcome; // non-volatile, protected by state reads/writes + /** The thread running the callable; CASed during run() */ + private volatile Thread runner; + /** Treiber stack of waiting threads */ + private volatile WaitNode waiters; + + /** + * Returns result or throws exception for completed task. + * + * @param s completed state value + */ + private V report(int s) throws ExecutionException { + Object x = outcome; + if (s == NORMAL) { + @SuppressWarnings("unchecked") V v = (V)x; + return v; + } + if (s >= CANCELLED) + throw new CancellationException(); + throw new ExecutionException((Throwable)x); + } /** - * Creates a <tt>FutureTask</tt> that will, upon running, execute the - * given <tt>Callable</tt>. + * Creates a {@code FutureTask} that will, upon running, execute the + * given {@code Callable}. * * @param callable the callable task - * @throws NullPointerException if callable is null + * @throws NullPointerException if the callable is null */ public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); - sync = new Sync(callable); + this.callable = callable; + this.state = NEW; // ensure visibility of callable } /** - * Creates a <tt>FutureTask</tt> that will, upon running, execute the - * given <tt>Runnable</tt>, and arrange that <tt>get</tt> will return the + * Creates a {@code FutureTask} that will, upon running, execute the + * given {@code Runnable}, and arrange that {@code get} will return the * given result on successful completion. * * @param runnable the runnable task @@ -57,29 +118,46 @@ public class FutureTask<V> implements RunnableFuture<V> { * you don't need a particular result, consider using * constructions of the form: * {@code Future<?> f = new FutureTask<Void>(runnable, null)} - * @throws NullPointerException if runnable is null + * @throws NullPointerException if the runnable is null */ public FutureTask(Runnable runnable, V result) { - sync = new Sync(Executors.callable(runnable, result)); + this.callable = Executors.callable(runnable, result); + this.state = NEW; // ensure visibility of callable } public boolean isCancelled() { - return sync.innerIsCancelled(); + return state >= CANCELLED; } public boolean isDone() { - return sync.innerIsDone(); + return state != NEW; } public boolean cancel(boolean mayInterruptIfRunning) { - return sync.innerCancel(mayInterruptIfRunning); + if (state != NEW) + return false; + if (mayInterruptIfRunning) { + if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, INTERRUPTING)) + return false; + Thread t = runner; + if (t != null) + t.interrupt(); + UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED); // final state + } + else if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, CANCELLED)) + return false; + finishCompletion(); + return true; } /** * @throws CancellationException {@inheritDoc} */ public V get() throws InterruptedException, ExecutionException { - return sync.innerGet(); + int s = state; + if (s <= COMPLETING) + s = awaitDone(false, 0L); + return report(s); } /** @@ -87,12 +165,18 @@ public class FutureTask<V> implements RunnableFuture<V> { */ public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { - return sync.innerGet(unit.toNanos(timeout)); + if (unit == null) + throw new NullPointerException(); + int s = state; + if (s <= COMPLETING && + (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING) + throw new TimeoutException(); + return report(s); } /** * Protected method invoked when this task transitions to state - * <tt>isDone</tt> (whether normally or via cancellation). The + * {@code isDone} (whether normally or via cancellation). The * default implementation does nothing. Subclasses may override * this method to invoke completion callbacks or perform * bookkeeping. Note that you can query status inside the @@ -102,230 +186,268 @@ public class FutureTask<V> implements RunnableFuture<V> { protected void done() { } /** - * Sets the result of this Future to the given value unless + * Sets the result of this future to the given value unless * this future has already been set or has been cancelled. - * This method is invoked internally by the <tt>run</tt> method + * + * <p>This method is invoked internally by the {@link #run} method * upon successful completion of the computation. + * * @param v the value */ protected void set(V v) { - sync.innerSet(v); + if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { + outcome = v; + UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state + finishCompletion(); + } } /** - * Causes this future to report an <tt>ExecutionException</tt> - * with the given throwable as its cause, unless this Future has + * Causes this future to report an {@link ExecutionException} + * with the given throwable as its cause, unless this future has * already been set or has been cancelled. - * This method is invoked internally by the <tt>run</tt> method + * + * <p>This method is invoked internally by the {@link #run} method * upon failure of the computation. + * * @param t the cause of failure */ protected void setException(Throwable t) { - sync.innerSetException(t); + if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { + outcome = t; + UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state + finishCompletion(); + } } - // The following (duplicated) doc comment can be removed once - // - // 6270645: Javadoc comments should be inherited from most derived - // superinterface or superclass - // is fixed. - /** - * Sets this Future to the result of its computation - * unless it has been cancelled. - */ public void run() { - sync.innerRun(); + if (state != NEW || + !UNSAFE.compareAndSwapObject(this, runnerOffset, + null, Thread.currentThread())) + return; + try { + Callable<V> c = callable; + if (c != null && state == NEW) { + V result; + boolean ran; + try { + result = c.call(); + ran = true; + } catch (Throwable ex) { + result = null; + ran = false; + setException(ex); + } + if (ran) + set(result); + } + } finally { + // runner must be non-null until state is settled to + // prevent concurrent calls to run() + runner = null; + // state must be re-read after nulling runner to prevent + // leaked interrupts + int s = state; + if (s >= INTERRUPTING) + handlePossibleCancellationInterrupt(s); + } } /** * Executes the computation without setting its result, and then - * resets this Future to initial state, failing to do so if the + * resets this future to initial state, failing to do so if the * computation encounters an exception or is cancelled. This is * designed for use with tasks that intrinsically execute more * than once. + * * @return true if successfully run and reset */ protected boolean runAndReset() { - return sync.innerRunAndReset(); + if (state != NEW || + !UNSAFE.compareAndSwapObject(this, runnerOffset, + null, Thread.currentThread())) + return false; + boolean ran = false; + int s = state; + try { + Callable<V> c = callable; + if (c != null && s == NEW) { + try { + c.call(); // don't set result + ran = true; + } catch (Throwable ex) { + setException(ex); + } + } + } finally { + // runner must be non-null until state is settled to + // prevent concurrent calls to run() + runner = null; + // state must be re-read after nulling runner to prevent + // leaked interrupts + s = state; + if (s >= INTERRUPTING) + handlePossibleCancellationInterrupt(s); + } + return ran && s == NEW; } /** - * Synchronization control for FutureTask. Note that this must be - * a non-static inner class in order to invoke the protected - * <tt>done</tt> method. For clarity, all inner class support - * methods are same as outer, prefixed with "inner". - * - * Uses AQS sync state to represent run status + * Ensures that any interrupt from a possible cancel(true) is only + * delivered to a task while in run or runAndReset. */ - private final class Sync extends AbstractQueuedSynchronizer { - private static final long serialVersionUID = -7828117401763700385L; - - /** State value representing that task is ready to run */ - private static final int READY = 0; - /** State value representing that task is running */ - private static final int RUNNING = 1; - /** State value representing that task ran */ - private static final int RAN = 2; - /** State value representing that task was cancelled */ - private static final int CANCELLED = 4; - - /** The underlying callable */ - private final Callable<V> callable; - /** The result to return from get() */ - private V result; - /** The exception to throw from get() */ - private Throwable exception; - - /** - * The thread running task. When nulled after set/cancel, this - * indicates that the results are accessible. Must be - * volatile, to ensure visibility upon completion. - */ - private volatile Thread runner; - - Sync(Callable<V> callable) { - this.callable = callable; - } - - private boolean ranOrCancelled(int state) { - return (state & (RAN | CANCELLED)) != 0; - } - - /** - * Implements AQS base acquire to succeed if ran or cancelled - */ - protected int tryAcquireShared(int ignore) { - return innerIsDone() ? 1 : -1; - } - - /** - * Implements AQS base release to always signal after setting - * final done status by nulling runner thread. - */ - protected boolean tryReleaseShared(int ignore) { - runner = null; - return true; - } - - boolean innerIsCancelled() { - return getState() == CANCELLED; - } - - boolean innerIsDone() { - return ranOrCancelled(getState()) && runner == null; - } - - V innerGet() throws InterruptedException, ExecutionException { - acquireSharedInterruptibly(0); - if (getState() == CANCELLED) - throw new CancellationException(); - if (exception != null) - throw new ExecutionException(exception); - return result; - } + private void handlePossibleCancellationInterrupt(int s) { + // It is possible for our interrupter to stall before getting a + // chance to interrupt us. Let's spin-wait patiently. + if (s == INTERRUPTING) + while (state == INTERRUPTING) + Thread.yield(); // wait out pending interrupt + + // assert state == INTERRUPTED; + + // We want to clear any interrupt we may have received from + // cancel(true). However, it is permissible to use interrupts + // as an independent mechanism for a task to communicate with + // its caller, and there is no way to clear only the + // cancellation interrupt. + // + // Thread.interrupted(); + } - V innerGet(long nanosTimeout) throws InterruptedException, ExecutionException, TimeoutException { - if (!tryAcquireSharedNanos(0, nanosTimeout)) - throw new TimeoutException(); - if (getState() == CANCELLED) - throw new CancellationException(); - if (exception != null) - throw new ExecutionException(exception); - return result; - } + /** + * Simple linked list nodes to record waiting threads in a Treiber + * stack. See other classes such as Phaser and SynchronousQueue + * for more detailed explanation. + */ + static final class WaitNode { + volatile Thread thread; + volatile WaitNode next; + WaitNode() { thread = Thread.currentThread(); } + } - void innerSet(V v) { - for (;;) { - int s = getState(); - if (s == RAN) - return; - if (s == CANCELLED) { - // aggressively release to set runner to null, - // in case we are racing with a cancel request - // that will try to interrupt runner - releaseShared(0); - return; - } - if (compareAndSetState(s, RAN)) { - result = v; - releaseShared(0); - done(); - return; + /** + * Removes and signals all waiting threads, invokes done(), and + * nulls out callable. + */ + private void finishCompletion() { + // assert state > COMPLETING; + for (WaitNode q; (q = waiters) != null;) { + if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) { + for (;;) { + Thread t = q.thread; + if (t != null) { + q.thread = null; + LockSupport.unpark(t); + } + WaitNode next = q.next; + if (next == null) + break; + q.next = null; // unlink to help gc + q = next; } + break; } } - void innerSetException(Throwable t) { - for (;;) { - int s = getState(); - if (s == RAN) - return; - if (s == CANCELLED) { - // aggressively release to set runner to null, - // in case we are racing with a cancel request - // that will try to interrupt runner - releaseShared(0); - return; - } - if (compareAndSetState(s, RAN)) { - exception = t; - releaseShared(0); - done(); - return; - } + done(); + + callable = null; // to reduce footprint + } + + /** + * Awaits completion or aborts on interrupt or timeout. + * + * @param timed true if use timed waits + * @param nanos time to wait, if timed + * @return state upon completion + */ + private int awaitDone(boolean timed, long nanos) + throws InterruptedException { + long last = timed ? System.nanoTime() : 0L; + WaitNode q = null; + boolean queued = false; + for (;;) { + if (Thread.interrupted()) { + removeWaiter(q); + throw new InterruptedException(); } - } - boolean innerCancel(boolean mayInterruptIfRunning) { - for (;;) { - int s = getState(); - if (ranOrCancelled(s)) - return false; - if (compareAndSetState(s, CANCELLED)) - break; + int s = state; + if (s > COMPLETING) { + if (q != null) + q.thread = null; + return s; } - if (mayInterruptIfRunning) { - Thread r = runner; - if (r != null) - r.interrupt(); + else if (q == null) + q = new WaitNode(); + else if (!queued) + queued = UNSAFE.compareAndSwapObject(this, waitersOffset, + q.next = waiters, q); + else if (timed) { + long now = System.nanoTime(); + if ((nanos -= (now - last)) <= 0L) { + removeWaiter(q); + return state; + } + last = now; + LockSupport.parkNanos(this, nanos); } - releaseShared(0); - done(); - return true; + else + LockSupport.park(this); } + } - void innerRun() { - if (!compareAndSetState(READY, RUNNING)) - return; - - runner = Thread.currentThread(); - if (getState() == RUNNING) { // recheck after setting thread - V result; - try { - result = callable.call(); - } catch (Throwable ex) { - setException(ex); - return; + /** + * Tries to unlink a timed-out or interrupted wait node to avoid + * accumulating garbage. Internal nodes are simply unspliced + * without CAS since it is harmless if they are traversed anyway + * by releasers. To avoid effects of unsplicing from already + * removed nodes, the list is retraversed in case of an apparent + * race. This is slow when there are a lot of nodes, but we don't + * expect lists to be long enough to outweigh higher-overhead + * schemes. + */ + private void removeWaiter(WaitNode node) { + if (node != null) { + node.thread = null; + retry: + for (;;) { // restart on removeWaiter race + for (WaitNode pred = null, q = waiters, s; q != null; q = s) { + s = q.next; + if (q.thread != null) + pred = q; + else if (pred != null) { + pred.next = s; + if (pred.thread == null) // check for race + continue retry; + } + else if (!UNSAFE.compareAndSwapObject(this, waitersOffset, + q, s)) + continue retry; } - set(result); - } else { - releaseShared(0); // cancel + break; } } + } - boolean innerRunAndReset() { - if (!compareAndSetState(READY, RUNNING)) - return false; - try { - runner = Thread.currentThread(); - if (getState() == RUNNING) - callable.call(); // don't set result - runner = null; - return compareAndSetState(RUNNING, READY); - } catch (Throwable ex) { - setException(ex); - return false; - } + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE; + private static final long stateOffset; + private static final long runnerOffset; + private static final long waitersOffset; + static { + try { + UNSAFE = sun.misc.Unsafe.getUnsafe(); + Class<?> k = FutureTask.class; + stateOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("state")); + runnerOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("runner")); + waitersOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("waiters")); + } catch (Exception e) { + throw new Error(e); } } + } diff --git a/luni/src/main/java/java/util/concurrent/LinkedBlockingDeque.java b/luni/src/main/java/java/util/concurrent/LinkedBlockingDeque.java index 8c01e71..6f32c47 100644 --- a/luni/src/main/java/java/util/concurrent/LinkedBlockingDeque.java +++ b/luni/src/main/java/java/util/concurrent/LinkedBlockingDeque.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; @@ -13,6 +13,10 @@ import java.util.NoSuchElementException; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; +// BEGIN android-note +// removed link to collections framework docs +// END android-note + /** * An optionally-bounded {@linkplain BlockingDeque blocking deque} based on * linked nodes. @@ -34,10 +38,6 @@ import java.util.concurrent.locks.ReentrantLock; * <em>optional</em> methods of the {@link Collection} and {@link * Iterator} interfaces. * - * <p>This class is a member of the - * <a href="{@docRoot}/../technotes/guides/collections/index.html"> - * Java Collections Framework</a>. - * * @since 1.6 * @author Doug Lea * @param <E> the type of elements held in this collection @@ -590,7 +590,7 @@ public class LinkedBlockingDeque<E> /** * Inserts the specified element at the end of this deque unless it would * violate capacity restrictions. When using a capacity-restricted deque, - * it is generally preferable to use method {@link #offer offer}. + * it is generally preferable to use method {@link #offer(Object) offer}. * * <p>This method is equivalent to {@link #addLast}. * @@ -713,6 +713,8 @@ public class LinkedBlockingDeque<E> throw new NullPointerException(); if (c == this) throw new IllegalArgumentException(); + if (maxElements <= 0) + return 0; final ReentrantLock lock = this.lock; lock.lock(); try { @@ -891,8 +893,7 @@ public class LinkedBlockingDeque<E> * The following code can be used to dump the deque into a newly * allocated array of {@code String}: * - * <pre> - * String[] y = x.toArray(new String[0]);</pre> + * <pre> {@code String[] y = x.toArray(new String[0]);}</pre> * * Note that {@code toArray(new Object[0])} is identical in function to * {@code toArray()}. @@ -1014,7 +1015,7 @@ public class LinkedBlockingDeque<E> /** * The next node to return in next() */ - Node<E> next; + Node<E> next; /** * nextItem holds on to item fields because once we claim that @@ -1122,7 +1123,7 @@ public class LinkedBlockingDeque<E> } /** - * Save the state of this deque to a stream (that is, serialize it). + * Saves the state of this deque to a stream (that is, serializes it). * * @serialData The capacity (int), followed by elements (each an * {@code Object}) in the proper order, followed by a null @@ -1146,8 +1147,8 @@ public class LinkedBlockingDeque<E> } /** - * Reconstitute this deque from a stream (that is, - * deserialize it). + * Reconstitutes this deque from a stream (that is, deserializes it). + * * @param s the stream */ private void readObject(java.io.ObjectInputStream s) diff --git a/luni/src/main/java/java/util/concurrent/LinkedBlockingQueue.java b/luni/src/main/java/java/util/concurrent/LinkedBlockingQueue.java index d06c737..e8c9edb 100644 --- a/luni/src/main/java/java/util/concurrent/LinkedBlockingQueue.java +++ b/luni/src/main/java/java/util/concurrent/LinkedBlockingQueue.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; @@ -106,13 +106,13 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> private final int capacity; /** Current number of elements */ - private final AtomicInteger count = new AtomicInteger(0); + private final AtomicInteger count = new AtomicInteger(); /** * Head of linked list. * Invariant: head.item == null */ - private transient Node<E> head; + transient Node<E> head; /** * Tail of linked list. @@ -303,7 +303,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> // Note: convention in all put/take/etc is to preset local var // holding count negative to indicate failure unless set. int c = -1; - Node<E> node = new Node(e); + Node<E> node = new Node<E>(e); final ReentrantLock putLock = this.putLock; final AtomicInteger count = this.count; putLock.lockInterruptibly(); @@ -383,7 +383,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> if (count.get() == capacity) return false; int c = -1; - Node<E> node = new Node(e); + Node<E> node = new Node<E>(e); final ReentrantLock putLock = this.putLock; putLock.lock(); try { @@ -601,8 +601,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> * The following code can be used to dump the queue into a newly * allocated array of {@code String}: * - * <pre> - * String[] y = x.toArray(new String[0]);</pre> + * <pre> {@code String[] y = x.toArray(new String[0]);}</pre> * * Note that {@code toArray(new Object[0])} is identical in function to * {@code toArray()}. @@ -699,6 +698,8 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> throw new NullPointerException(); if (c == this) throw new IllegalArgumentException(); + if (maxElements <= 0) + return 0; boolean signalNotFull = false; final ReentrantLock takeLock = this.takeLock; takeLock.lock(); @@ -746,7 +747,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> * @return an iterator over the elements in this queue in proper sequence */ public Iterator<E> iterator() { - return new Itr(); + return new Itr(); } private class Itr implements Iterator<E> { @@ -829,7 +830,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> } /** - * Save the state to a stream (that is, serialize it). + * Saves the state to a stream (that is, serializes it). * * @serialData The capacity is emitted (int), followed by all of * its elements (each an {@code Object}) in the proper order, @@ -856,8 +857,7 @@ public class LinkedBlockingQueue<E> extends AbstractQueue<E> } /** - * Reconstitute this queue instance from a stream (that is, - * deserialize it). + * Reconstitutes this queue from a stream (that is, deserializes it). * * @param s the stream */ diff --git a/luni/src/main/java/java/util/concurrent/LinkedTransferQueue.java b/luni/src/main/java/java/util/concurrent/LinkedTransferQueue.java new file mode 100644 index 0000000..2a3446e --- /dev/null +++ b/luni/src/main/java/java/util/concurrent/LinkedTransferQueue.java @@ -0,0 +1,1323 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +package java.util.concurrent; + +import java.util.AbstractQueue; +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Queue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.LockSupport; + +// BEGIN android-note +// removed link to collections framework docs +// END android-note + +/** + * An unbounded {@link TransferQueue} based on linked nodes. + * This queue orders elements FIFO (first-in-first-out) with respect + * to any given producer. The <em>head</em> of the queue is that + * element that has been on the queue the longest time for some + * producer. The <em>tail</em> of the queue is that element that has + * been on the queue the shortest time for some producer. + * + * <p>Beware that, unlike in most collections, the {@code size} method + * is <em>NOT</em> a constant-time operation. Because of the + * asynchronous nature of these queues, determining the current number + * of elements requires a traversal of the elements, and so may report + * inaccurate results if this collection is modified during traversal. + * Additionally, the bulk operations {@code addAll}, + * {@code removeAll}, {@code retainAll}, {@code containsAll}, + * {@code equals}, and {@code toArray} are <em>not</em> guaranteed + * to be performed atomically. For example, an iterator operating + * concurrently with an {@code addAll} operation might view only some + * of the added elements. + * + * <p>This class and its iterator implement all of the + * <em>optional</em> methods of the {@link Collection} and {@link + * Iterator} interfaces. + * + * <p>Memory consistency effects: As with other concurrent + * collections, actions in a thread prior to placing an object into a + * {@code LinkedTransferQueue} + * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a> + * actions subsequent to the access or removal of that element from + * the {@code LinkedTransferQueue} in another thread. + * + * @since 1.7 + * @hide + * @author Doug Lea + * @param <E> the type of elements held in this collection + */ +public class LinkedTransferQueue<E> extends AbstractQueue<E> + implements TransferQueue<E>, java.io.Serializable { + private static final long serialVersionUID = -3223113410248163686L; + + /* + * *** Overview of Dual Queues with Slack *** + * + * Dual Queues, introduced by Scherer and Scott + * (http://www.cs.rice.edu/~wns1/papers/2004-DISC-DDS.pdf) are + * (linked) queues in which nodes may represent either data or + * requests. When a thread tries to enqueue a data node, but + * encounters a request node, it instead "matches" and removes it; + * and vice versa for enqueuing requests. Blocking Dual Queues + * arrange that threads enqueuing unmatched requests block until + * other threads provide the match. Dual Synchronous Queues (see + * Scherer, Lea, & Scott + * http://www.cs.rochester.edu/u/scott/papers/2009_Scherer_CACM_SSQ.pdf) + * additionally arrange that threads enqueuing unmatched data also + * block. Dual Transfer Queues support all of these modes, as + * dictated by callers. + * + * A FIFO dual queue may be implemented using a variation of the + * Michael & Scott (M&S) lock-free queue algorithm + * (http://www.cs.rochester.edu/u/scott/papers/1996_PODC_queues.pdf). + * It maintains two pointer fields, "head", pointing to a + * (matched) node that in turn points to the first actual + * (unmatched) queue node (or null if empty); and "tail" that + * points to the last node on the queue (or again null if + * empty). For example, here is a possible queue with four data + * elements: + * + * head tail + * | | + * v v + * M -> U -> U -> U -> U + * + * The M&S queue algorithm is known to be prone to scalability and + * overhead limitations when maintaining (via CAS) these head and + * tail pointers. This has led to the development of + * contention-reducing variants such as elimination arrays (see + * Moir et al http://portal.acm.org/citation.cfm?id=1074013) and + * optimistic back pointers (see Ladan-Mozes & Shavit + * http://people.csail.mit.edu/edya/publications/OptimisticFIFOQueue-journal.pdf). + * However, the nature of dual queues enables a simpler tactic for + * improving M&S-style implementations when dual-ness is needed. + * + * In a dual queue, each node must atomically maintain its match + * status. While there are other possible variants, we implement + * this here as: for a data-mode node, matching entails CASing an + * "item" field from a non-null data value to null upon match, and + * vice-versa for request nodes, CASing from null to a data + * value. (Note that the linearization properties of this style of + * queue are easy to verify -- elements are made available by + * linking, and unavailable by matching.) Compared to plain M&S + * queues, this property of dual queues requires one additional + * successful atomic operation per enq/deq pair. But it also + * enables lower cost variants of queue maintenance mechanics. (A + * variation of this idea applies even for non-dual queues that + * support deletion of interior elements, such as + * j.u.c.ConcurrentLinkedQueue.) + * + * Once a node is matched, its match status can never again + * change. We may thus arrange that the linked list of them + * contain a prefix of zero or more matched nodes, followed by a + * suffix of zero or more unmatched nodes. (Note that we allow + * both the prefix and suffix to be zero length, which in turn + * means that we do not use a dummy header.) If we were not + * concerned with either time or space efficiency, we could + * correctly perform enqueue and dequeue operations by traversing + * from a pointer to the initial node; CASing the item of the + * first unmatched node on match and CASing the next field of the + * trailing node on appends. (Plus some special-casing when + * initially empty). While this would be a terrible idea in + * itself, it does have the benefit of not requiring ANY atomic + * updates on head/tail fields. + * + * We introduce here an approach that lies between the extremes of + * never versus always updating queue (head and tail) pointers. + * This offers a tradeoff between sometimes requiring extra + * traversal steps to locate the first and/or last unmatched + * nodes, versus the reduced overhead and contention of fewer + * updates to queue pointers. For example, a possible snapshot of + * a queue is: + * + * head tail + * | | + * v v + * M -> M -> U -> U -> U -> U + * + * The best value for this "slack" (the targeted maximum distance + * between the value of "head" and the first unmatched node, and + * similarly for "tail") is an empirical matter. We have found + * that using very small constants in the range of 1-3 work best + * over a range of platforms. Larger values introduce increasing + * costs of cache misses and risks of long traversal chains, while + * smaller values increase CAS contention and overhead. + * + * Dual queues with slack differ from plain M&S dual queues by + * virtue of only sometimes updating head or tail pointers when + * matching, appending, or even traversing nodes; in order to + * maintain a targeted slack. The idea of "sometimes" may be + * operationalized in several ways. The simplest is to use a + * per-operation counter incremented on each traversal step, and + * to try (via CAS) to update the associated queue pointer + * whenever the count exceeds a threshold. Another, that requires + * more overhead, is to use random number generators to update + * with a given probability per traversal step. + * + * In any strategy along these lines, because CASes updating + * fields may fail, the actual slack may exceed targeted + * slack. However, they may be retried at any time to maintain + * targets. Even when using very small slack values, this + * approach works well for dual queues because it allows all + * operations up to the point of matching or appending an item + * (hence potentially allowing progress by another thread) to be + * read-only, thus not introducing any further contention. As + * described below, we implement this by performing slack + * maintenance retries only after these points. + * + * As an accompaniment to such techniques, traversal overhead can + * be further reduced without increasing contention of head + * pointer updates: Threads may sometimes shortcut the "next" link + * path from the current "head" node to be closer to the currently + * known first unmatched node, and similarly for tail. Again, this + * may be triggered with using thresholds or randomization. + * + * These ideas must be further extended to avoid unbounded amounts + * of costly-to-reclaim garbage caused by the sequential "next" + * links of nodes starting at old forgotten head nodes: As first + * described in detail by Boehm + * (http://portal.acm.org/citation.cfm?doid=503272.503282) if a GC + * delays noticing that any arbitrarily old node has become + * garbage, all newer dead nodes will also be unreclaimed. + * (Similar issues arise in non-GC environments.) To cope with + * this in our implementation, upon CASing to advance the head + * pointer, we set the "next" link of the previous head to point + * only to itself; thus limiting the length of connected dead lists. + * (We also take similar care to wipe out possibly garbage + * retaining values held in other Node fields.) However, doing so + * adds some further complexity to traversal: If any "next" + * pointer links to itself, it indicates that the current thread + * has lagged behind a head-update, and so the traversal must + * continue from the "head". Traversals trying to find the + * current tail starting from "tail" may also encounter + * self-links, in which case they also continue at "head". + * + * It is tempting in slack-based scheme to not even use CAS for + * updates (similarly to Ladan-Mozes & Shavit). However, this + * cannot be done for head updates under the above link-forgetting + * mechanics because an update may leave head at a detached node. + * And while direct writes are possible for tail updates, they + * increase the risk of long retraversals, and hence long garbage + * chains, which can be much more costly than is worthwhile + * considering that the cost difference of performing a CAS vs + * write is smaller when they are not triggered on each operation + * (especially considering that writes and CASes equally require + * additional GC bookkeeping ("write barriers") that are sometimes + * more costly than the writes themselves because of contention). + * + * *** Overview of implementation *** + * + * We use a threshold-based approach to updates, with a slack + * threshold of two -- that is, we update head/tail when the + * current pointer appears to be two or more steps away from the + * first/last node. The slack value is hard-wired: a path greater + * than one is naturally implemented by checking equality of + * traversal pointers except when the list has only one element, + * in which case we keep slack threshold at one. Avoiding tracking + * explicit counts across method calls slightly simplifies an + * already-messy implementation. Using randomization would + * probably work better if there were a low-quality dirt-cheap + * per-thread one available, but even ThreadLocalRandom is too + * heavy for these purposes. + * + * With such a small slack threshold value, it is not worthwhile + * to augment this with path short-circuiting (i.e., unsplicing + * interior nodes) except in the case of cancellation/removal (see + * below). + * + * We allow both the head and tail fields to be null before any + * nodes are enqueued; initializing upon first append. This + * simplifies some other logic, as well as providing more + * efficient explicit control paths instead of letting JVMs insert + * implicit NullPointerExceptions when they are null. While not + * currently fully implemented, we also leave open the possibility + * of re-nulling these fields when empty (which is complicated to + * arrange, for little benefit.) + * + * All enqueue/dequeue operations are handled by the single method + * "xfer" with parameters indicating whether to act as some form + * of offer, put, poll, take, or transfer (each possibly with + * timeout). The relative complexity of using one monolithic + * method outweighs the code bulk and maintenance problems of + * using separate methods for each case. + * + * Operation consists of up to three phases. The first is + * implemented within method xfer, the second in tryAppend, and + * the third in method awaitMatch. + * + * 1. Try to match an existing node + * + * Starting at head, skip already-matched nodes until finding + * an unmatched node of opposite mode, if one exists, in which + * case matching it and returning, also if necessary updating + * head to one past the matched node (or the node itself if the + * list has no other unmatched nodes). If the CAS misses, then + * a loop retries advancing head by two steps until either + * success or the slack is at most two. By requiring that each + * attempt advances head by two (if applicable), we ensure that + * the slack does not grow without bound. Traversals also check + * if the initial head is now off-list, in which case they + * start at the new head. + * + * If no candidates are found and the call was untimed + * poll/offer, (argument "how" is NOW) return. + * + * 2. Try to append a new node (method tryAppend) + * + * Starting at current tail pointer, find the actual last node + * and try to append a new node (or if head was null, establish + * the first node). Nodes can be appended only if their + * predecessors are either already matched or are of the same + * mode. If we detect otherwise, then a new node with opposite + * mode must have been appended during traversal, so we must + * restart at phase 1. The traversal and update steps are + * otherwise similar to phase 1: Retrying upon CAS misses and + * checking for staleness. In particular, if a self-link is + * encountered, then we can safely jump to a node on the list + * by continuing the traversal at current head. + * + * On successful append, if the call was ASYNC, return. + * + * 3. Await match or cancellation (method awaitMatch) + * + * Wait for another thread to match node; instead cancelling if + * the current thread was interrupted or the wait timed out. On + * multiprocessors, we use front-of-queue spinning: If a node + * appears to be the first unmatched node in the queue, it + * spins a bit before blocking. In either case, before blocking + * it tries to unsplice any nodes between the current "head" + * and the first unmatched node. + * + * Front-of-queue spinning vastly improves performance of + * heavily contended queues. And so long as it is relatively + * brief and "quiet", spinning does not much impact performance + * of less-contended queues. During spins threads check their + * interrupt status and generate a thread-local random number + * to decide to occasionally perform a Thread.yield. While + * yield has underdefined specs, we assume that it might help, + * and will not hurt, in limiting impact of spinning on busy + * systems. We also use smaller (1/2) spins for nodes that are + * not known to be front but whose predecessors have not + * blocked -- these "chained" spins avoid artifacts of + * front-of-queue rules which otherwise lead to alternating + * nodes spinning vs blocking. Further, front threads that + * represent phase changes (from data to request node or vice + * versa) compared to their predecessors receive additional + * chained spins, reflecting longer paths typically required to + * unblock threads during phase changes. + * + * + * ** Unlinking removed interior nodes ** + * + * In addition to minimizing garbage retention via self-linking + * described above, we also unlink removed interior nodes. These + * may arise due to timed out or interrupted waits, or calls to + * remove(x) or Iterator.remove. Normally, given a node that was + * at one time known to be the predecessor of some node s that is + * to be removed, we can unsplice s by CASing the next field of + * its predecessor if it still points to s (otherwise s must + * already have been removed or is now offlist). But there are two + * situations in which we cannot guarantee to make node s + * unreachable in this way: (1) If s is the trailing node of list + * (i.e., with null next), then it is pinned as the target node + * for appends, so can only be removed later after other nodes are + * appended. (2) We cannot necessarily unlink s given a + * predecessor node that is matched (including the case of being + * cancelled): the predecessor may already be unspliced, in which + * case some previous reachable node may still point to s. + * (For further explanation see Herlihy & Shavit "The Art of + * Multiprocessor Programming" chapter 9). Although, in both + * cases, we can rule out the need for further action if either s + * or its predecessor are (or can be made to be) at, or fall off + * from, the head of list. + * + * Without taking these into account, it would be possible for an + * unbounded number of supposedly removed nodes to remain + * reachable. Situations leading to such buildup are uncommon but + * can occur in practice; for example when a series of short timed + * calls to poll repeatedly time out but never otherwise fall off + * the list because of an untimed call to take at the front of the + * queue. + * + * When these cases arise, rather than always retraversing the + * entire list to find an actual predecessor to unlink (which + * won't help for case (1) anyway), we record a conservative + * estimate of possible unsplice failures (in "sweepVotes"). + * We trigger a full sweep when the estimate exceeds a threshold + * ("SWEEP_THRESHOLD") indicating the maximum number of estimated + * removal failures to tolerate before sweeping through, unlinking + * cancelled nodes that were not unlinked upon initial removal. + * We perform sweeps by the thread hitting threshold (rather than + * background threads or by spreading work to other threads) + * because in the main contexts in which removal occurs, the + * caller is already timed-out, cancelled, or performing a + * potentially O(n) operation (e.g. remove(x)), none of which are + * time-critical enough to warrant the overhead that alternatives + * would impose on other threads. + * + * Because the sweepVotes estimate is conservative, and because + * nodes become unlinked "naturally" as they fall off the head of + * the queue, and because we allow votes to accumulate even while + * sweeps are in progress, there are typically significantly fewer + * such nodes than estimated. Choice of a threshold value + * balances the likelihood of wasted effort and contention, versus + * providing a worst-case bound on retention of interior nodes in + * quiescent queues. The value defined below was chosen + * empirically to balance these under various timeout scenarios. + * + * Note that we cannot self-link unlinked interior nodes during + * sweeps. However, the associated garbage chains terminate when + * some successor ultimately falls off the head of the list and is + * self-linked. + */ + + /** True if on multiprocessor */ + private static final boolean MP = + Runtime.getRuntime().availableProcessors() > 1; + + /** + * The number of times to spin (with randomly interspersed calls + * to Thread.yield) on multiprocessor before blocking when a node + * is apparently the first waiter in the queue. See above for + * explanation. Must be a power of two. The value is empirically + * derived -- it works pretty well across a variety of processors, + * numbers of CPUs, and OSes. + */ + private static final int FRONT_SPINS = 1 << 7; + + /** + * The number of times to spin before blocking when a node is + * preceded by another node that is apparently spinning. Also + * serves as an increment to FRONT_SPINS on phase changes, and as + * base average frequency for yielding during spins. Must be a + * power of two. + */ + private static final int CHAINED_SPINS = FRONT_SPINS >>> 1; + + /** + * The maximum number of estimated removal failures (sweepVotes) + * to tolerate before sweeping through the queue unlinking + * cancelled nodes that were not unlinked upon initial + * removal. See above for explanation. The value must be at least + * two to avoid useless sweeps when removing trailing nodes. + */ + static final int SWEEP_THRESHOLD = 32; + + /** + * Queue nodes. Uses Object, not E, for items to allow forgetting + * them after use. Relies heavily on Unsafe mechanics to minimize + * unnecessary ordering constraints: Writes that are intrinsically + * ordered wrt other accesses or CASes use simple relaxed forms. + */ + static final class Node { + final boolean isData; // false if this is a request node + volatile Object item; // initially non-null if isData; CASed to match + volatile Node next; + volatile Thread waiter; // null until waiting + + // CAS methods for fields + final boolean casNext(Node cmp, Node val) { + return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val); + } + + final boolean casItem(Object cmp, Object val) { + // assert cmp == null || cmp.getClass() != Node.class; + return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val); + } + + /** + * Constructs a new node. Uses relaxed write because item can + * only be seen after publication via casNext. + */ + Node(Object item, boolean isData) { + UNSAFE.putObject(this, itemOffset, item); // relaxed write + this.isData = isData; + } + + /** + * Links node to itself to avoid garbage retention. Called + * only after CASing head field, so uses relaxed write. + */ + final void forgetNext() { + UNSAFE.putObject(this, nextOffset, this); + } + + /** + * Sets item to self and waiter to null, to avoid garbage + * retention after matching or cancelling. Uses relaxed writes + * because order is already constrained in the only calling + * contexts: item is forgotten only after volatile/atomic + * mechanics that extract items. Similarly, clearing waiter + * follows either CAS or return from park (if ever parked; + * else we don't care). + */ + final void forgetContents() { + UNSAFE.putObject(this, itemOffset, this); + UNSAFE.putObject(this, waiterOffset, null); + } + + /** + * Returns true if this node has been matched, including the + * case of artificial matches due to cancellation. + */ + final boolean isMatched() { + Object x = item; + return (x == this) || ((x == null) == isData); + } + + /** + * Returns true if this is an unmatched request node. + */ + final boolean isUnmatchedRequest() { + return !isData && item == null; + } + + /** + * Returns true if a node with the given mode cannot be + * appended to this node because this node is unmatched and + * has opposite data mode. + */ + final boolean cannotPrecede(boolean haveData) { + boolean d = isData; + Object x; + return d != haveData && (x = item) != this && (x != null) == d; + } + + /** + * Tries to artificially match a data node -- used by remove. + */ + final boolean tryMatchData() { + // assert isData; + Object x = item; + if (x != null && x != this && casItem(x, null)) { + LockSupport.unpark(waiter); + return true; + } + return false; + } + + private static final long serialVersionUID = -3375979862319811754L; + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE; + private static final long itemOffset; + private static final long nextOffset; + private static final long waiterOffset; + static { + try { + UNSAFE = sun.misc.Unsafe.getUnsafe(); + Class<?> k = Node.class; + itemOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("item")); + nextOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("next")); + waiterOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("waiter")); + } catch (Exception e) { + throw new Error(e); + } + } + } + + /** head of the queue; null until first enqueue */ + transient volatile Node head; + + /** tail of the queue; null until first append */ + private transient volatile Node tail; + + /** The number of apparent failures to unsplice removed nodes */ + private transient volatile int sweepVotes; + + // CAS methods for fields + private boolean casTail(Node cmp, Node val) { + return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val); + } + + private boolean casHead(Node cmp, Node val) { + return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val); + } + + private boolean casSweepVotes(int cmp, int val) { + return UNSAFE.compareAndSwapInt(this, sweepVotesOffset, cmp, val); + } + + /* + * Possible values for "how" argument in xfer method. + */ + private static final int NOW = 0; // for untimed poll, tryTransfer + private static final int ASYNC = 1; // for offer, put, add + private static final int SYNC = 2; // for transfer, take + private static final int TIMED = 3; // for timed poll, tryTransfer + + @SuppressWarnings("unchecked") + static <E> E cast(Object item) { + // assert item == null || item.getClass() != Node.class; + return (E) item; + } + + /** + * Implements all queuing methods. See above for explanation. + * + * @param e the item or null for take + * @param haveData true if this is a put, else a take + * @param how NOW, ASYNC, SYNC, or TIMED + * @param nanos timeout in nanosecs, used only if mode is TIMED + * @return an item if matched, else e + * @throws NullPointerException if haveData mode but e is null + */ + private E xfer(E e, boolean haveData, int how, long nanos) { + if (haveData && (e == null)) + throw new NullPointerException(); + Node s = null; // the node to append, if needed + + retry: + for (;;) { // restart on append race + + for (Node h = head, p = h; p != null;) { // find & match first node + boolean isData = p.isData; + Object item = p.item; + if (item != p && (item != null) == isData) { // unmatched + if (isData == haveData) // can't match + break; + if (p.casItem(item, e)) { // match + for (Node q = p; q != h;) { + Node n = q.next; // update by 2 unless singleton + if (head == h && casHead(h, n == null ? q : n)) { + h.forgetNext(); + break; + } // advance and retry + if ((h = head) == null || + (q = h.next) == null || !q.isMatched()) + break; // unless slack < 2 + } + LockSupport.unpark(p.waiter); + return LinkedTransferQueue.<E>cast(item); + } + } + Node n = p.next; + p = (p != n) ? n : (h = head); // Use head if p offlist + } + + if (how != NOW) { // No matches available + if (s == null) + s = new Node(e, haveData); + Node pred = tryAppend(s, haveData); + if (pred == null) + continue retry; // lost race vs opposite mode + if (how != ASYNC) + return awaitMatch(s, pred, e, (how == TIMED), nanos); + } + return e; // not waiting + } + } + + /** + * Tries to append node s as tail. + * + * @param s the node to append + * @param haveData true if appending in data mode + * @return null on failure due to losing race with append in + * different mode, else s's predecessor, or s itself if no + * predecessor + */ + private Node tryAppend(Node s, boolean haveData) { + for (Node t = tail, p = t;;) { // move p to last node and append + Node n, u; // temps for reads of next & tail + if (p == null && (p = head) == null) { + if (casHead(null, s)) + return s; // initialize + } + else if (p.cannotPrecede(haveData)) + return null; // lost race vs opposite mode + else if ((n = p.next) != null) // not last; keep traversing + p = p != t && t != (u = tail) ? (t = u) : // stale tail + (p != n) ? n : null; // restart if off list + else if (!p.casNext(null, s)) + p = p.next; // re-read on CAS failure + else { + if (p != t) { // update if slack now >= 2 + while ((tail != t || !casTail(t, s)) && + (t = tail) != null && + (s = t.next) != null && // advance and retry + (s = s.next) != null && s != t); + } + return p; + } + } + } + + /** + * Spins/yields/blocks until node s is matched or caller gives up. + * + * @param s the waiting node + * @param pred the predecessor of s, or s itself if it has no + * predecessor, or null if unknown (the null case does not occur + * in any current calls but may in possible future extensions) + * @param e the comparison value for checking match + * @param timed if true, wait only until timeout elapses + * @param nanos timeout in nanosecs, used only if timed is true + * @return matched item, or e if unmatched on interrupt or timeout + */ + private E awaitMatch(Node s, Node pred, E e, boolean timed, long nanos) { + long lastTime = timed ? System.nanoTime() : 0L; + Thread w = Thread.currentThread(); + int spins = -1; // initialized after first item and cancel checks + ThreadLocalRandom randomYields = null; // bound if needed + + for (;;) { + Object item = s.item; + if (item != e) { // matched + // assert item != s; + s.forgetContents(); // avoid garbage + return LinkedTransferQueue.<E>cast(item); + } + if ((w.isInterrupted() || (timed && nanos <= 0)) && + s.casItem(e, s)) { // cancel + unsplice(pred, s); + return e; + } + + if (spins < 0) { // establish spins at/near front + if ((spins = spinsFor(pred, s.isData)) > 0) + randomYields = ThreadLocalRandom.current(); + } + else if (spins > 0) { // spin + --spins; + if (randomYields.nextInt(CHAINED_SPINS) == 0) + Thread.yield(); // occasionally yield + } + else if (s.waiter == null) { + s.waiter = w; // request unpark then recheck + } + else if (timed) { + long now = System.nanoTime(); + if ((nanos -= now - lastTime) > 0) + LockSupport.parkNanos(this, nanos); + lastTime = now; + } + else { + LockSupport.park(this); + } + } + } + + /** + * Returns spin/yield value for a node with given predecessor and + * data mode. See above for explanation. + */ + private static int spinsFor(Node pred, boolean haveData) { + if (MP && pred != null) { + if (pred.isData != haveData) // phase change + return FRONT_SPINS + CHAINED_SPINS; + if (pred.isMatched()) // probably at front + return FRONT_SPINS; + if (pred.waiter == null) // pred apparently spinning + return CHAINED_SPINS; + } + return 0; + } + + /* -------------- Traversal methods -------------- */ + + /** + * Returns the successor of p, or the head node if p.next has been + * linked to self, which will only be true if traversing with a + * stale pointer that is now off the list. + */ + final Node succ(Node p) { + Node next = p.next; + return (p == next) ? head : next; + } + + /** + * Returns the first unmatched node of the given mode, or null if + * none. Used by methods isEmpty, hasWaitingConsumer. + */ + private Node firstOfMode(boolean isData) { + for (Node p = head; p != null; p = succ(p)) { + if (!p.isMatched()) + return (p.isData == isData) ? p : null; + } + return null; + } + + /** + * Returns the item in the first unmatched node with isData; or + * null if none. Used by peek. + */ + private E firstDataItem() { + for (Node p = head; p != null; p = succ(p)) { + Object item = p.item; + if (p.isData) { + if (item != null && item != p) + return LinkedTransferQueue.<E>cast(item); + } + else if (item == null) + return null; + } + return null; + } + + /** + * Traverses and counts unmatched nodes of the given mode. + * Used by methods size and getWaitingConsumerCount. + */ + private int countOfMode(boolean data) { + int count = 0; + for (Node p = head; p != null; ) { + if (!p.isMatched()) { + if (p.isData != data) + return 0; + if (++count == Integer.MAX_VALUE) // saturated + break; + } + Node n = p.next; + if (n != p) + p = n; + else { + count = 0; + p = head; + } + } + return count; + } + + final class Itr implements Iterator<E> { + private Node nextNode; // next node to return item for + private E nextItem; // the corresponding item + private Node lastRet; // last returned node, to support remove + private Node lastPred; // predecessor to unlink lastRet + + /** + * Moves to next node after prev, or first node if prev null. + */ + private void advance(Node prev) { + /* + * To track and avoid buildup of deleted nodes in the face + * of calls to both Queue.remove and Itr.remove, we must + * include variants of unsplice and sweep upon each + * advance: Upon Itr.remove, we may need to catch up links + * from lastPred, and upon other removes, we might need to + * skip ahead from stale nodes and unsplice deleted ones + * found while advancing. + */ + + Node r, b; // reset lastPred upon possible deletion of lastRet + if ((r = lastRet) != null && !r.isMatched()) + lastPred = r; // next lastPred is old lastRet + else if ((b = lastPred) == null || b.isMatched()) + lastPred = null; // at start of list + else { + Node s, n; // help with removal of lastPred.next + while ((s = b.next) != null && + s != b && s.isMatched() && + (n = s.next) != null && n != s) + b.casNext(s, n); + } + + this.lastRet = prev; + + for (Node p = prev, s, n;;) { + s = (p == null) ? head : p.next; + if (s == null) + break; + else if (s == p) { + p = null; + continue; + } + Object item = s.item; + if (s.isData) { + if (item != null && item != s) { + nextItem = LinkedTransferQueue.<E>cast(item); + nextNode = s; + return; + } + } + else if (item == null) + break; + // assert s.isMatched(); + if (p == null) + p = s; + else if ((n = s.next) == null) + break; + else if (s == n) + p = null; + else + p.casNext(s, n); + } + nextNode = null; + nextItem = null; + } + + Itr() { + advance(null); + } + + public final boolean hasNext() { + return nextNode != null; + } + + public final E next() { + Node p = nextNode; + if (p == null) throw new NoSuchElementException(); + E e = nextItem; + advance(p); + return e; + } + + public final void remove() { + final Node lastRet = this.lastRet; + if (lastRet == null) + throw new IllegalStateException(); + this.lastRet = null; + if (lastRet.tryMatchData()) + unsplice(lastPred, lastRet); + } + } + + /* -------------- Removal methods -------------- */ + + /** + * Unsplices (now or later) the given deleted/cancelled node with + * the given predecessor. + * + * @param pred a node that was at one time known to be the + * predecessor of s, or null or s itself if s is/was at head + * @param s the node to be unspliced + */ + final void unsplice(Node pred, Node s) { + s.forgetContents(); // forget unneeded fields + /* + * See above for rationale. Briefly: if pred still points to + * s, try to unlink s. If s cannot be unlinked, because it is + * trailing node or pred might be unlinked, and neither pred + * nor s are head or offlist, add to sweepVotes, and if enough + * votes have accumulated, sweep. + */ + if (pred != null && pred != s && pred.next == s) { + Node n = s.next; + if (n == null || + (n != s && pred.casNext(s, n) && pred.isMatched())) { + for (;;) { // check if at, or could be, head + Node h = head; + if (h == pred || h == s || h == null) + return; // at head or list empty + if (!h.isMatched()) + break; + Node hn = h.next; + if (hn == null) + return; // now empty + if (hn != h && casHead(h, hn)) + h.forgetNext(); // advance head + } + if (pred.next != pred && s.next != s) { // recheck if offlist + for (;;) { // sweep now if enough votes + int v = sweepVotes; + if (v < SWEEP_THRESHOLD) { + if (casSweepVotes(v, v + 1)) + break; + } + else if (casSweepVotes(v, 0)) { + sweep(); + break; + } + } + } + } + } + } + + /** + * Unlinks matched (typically cancelled) nodes encountered in a + * traversal from head. + */ + private void sweep() { + for (Node p = head, s, n; p != null && (s = p.next) != null; ) { + if (!s.isMatched()) + // Unmatched nodes are never self-linked + p = s; + else if ((n = s.next) == null) // trailing node is pinned + break; + else if (s == n) // stale + // No need to also check for p == s, since that implies s == n + p = head; + else + p.casNext(s, n); + } + } + + /** + * Main implementation of remove(Object) + */ + private boolean findAndRemove(Object e) { + if (e != null) { + for (Node pred = null, p = head; p != null; ) { + Object item = p.item; + if (p.isData) { + if (item != null && item != p && e.equals(item) && + p.tryMatchData()) { + unsplice(pred, p); + return true; + } + } + else if (item == null) + break; + pred = p; + if ((p = p.next) == pred) { // stale + pred = null; + p = head; + } + } + } + return false; + } + + + /** + * Creates an initially empty {@code LinkedTransferQueue}. + */ + public LinkedTransferQueue() { + } + + /** + * Creates a {@code LinkedTransferQueue} + * initially containing the elements of the given collection, + * added in traversal order of the collection's iterator. + * + * @param c the collection of elements to initially contain + * @throws NullPointerException if the specified collection or any + * of its elements are null + */ + public LinkedTransferQueue(Collection<? extends E> c) { + this(); + addAll(c); + } + + /** + * Inserts the specified element at the tail of this queue. + * As the queue is unbounded, this method will never block. + * + * @throws NullPointerException if the specified element is null + */ + public void put(E e) { + xfer(e, true, ASYNC, 0); + } + + /** + * Inserts the specified element at the tail of this queue. + * As the queue is unbounded, this method will never block or + * return {@code false}. + * + * @return {@code true} (as specified by + * {@link java.util.concurrent.BlockingQueue#offer(Object,long,TimeUnit) + * BlockingQueue.offer}) + * @throws NullPointerException if the specified element is null + */ + public boolean offer(E e, long timeout, TimeUnit unit) { + xfer(e, true, ASYNC, 0); + return true; + } + + /** + * Inserts the specified element at the tail of this queue. + * As the queue is unbounded, this method will never return {@code false}. + * + * @return {@code true} (as specified by {@link Queue#offer}) + * @throws NullPointerException if the specified element is null + */ + public boolean offer(E e) { + xfer(e, true, ASYNC, 0); + return true; + } + + /** + * Inserts the specified element at the tail of this queue. + * As the queue is unbounded, this method will never throw + * {@link IllegalStateException} or return {@code false}. + * + * @return {@code true} (as specified by {@link Collection#add}) + * @throws NullPointerException if the specified element is null + */ + public boolean add(E e) { + xfer(e, true, ASYNC, 0); + return true; + } + + /** + * Transfers the element to a waiting consumer immediately, if possible. + * + * <p>More precisely, transfers the specified element immediately + * if there exists a consumer already waiting to receive it (in + * {@link #take} or timed {@link #poll(long,TimeUnit) poll}), + * otherwise returning {@code false} without enqueuing the element. + * + * @throws NullPointerException if the specified element is null + */ + public boolean tryTransfer(E e) { + return xfer(e, true, NOW, 0) == null; + } + + /** + * Transfers the element to a consumer, waiting if necessary to do so. + * + * <p>More precisely, transfers the specified element immediately + * if there exists a consumer already waiting to receive it (in + * {@link #take} or timed {@link #poll(long,TimeUnit) poll}), + * else inserts the specified element at the tail of this queue + * and waits until the element is received by a consumer. + * + * @throws NullPointerException if the specified element is null + */ + public void transfer(E e) throws InterruptedException { + if (xfer(e, true, SYNC, 0) != null) { + Thread.interrupted(); // failure possible only due to interrupt + throw new InterruptedException(); + } + } + + /** + * Transfers the element to a consumer if it is possible to do so + * before the timeout elapses. + * + * <p>More precisely, transfers the specified element immediately + * if there exists a consumer already waiting to receive it (in + * {@link #take} or timed {@link #poll(long,TimeUnit) poll}), + * else inserts the specified element at the tail of this queue + * and waits until the element is received by a consumer, + * returning {@code false} if the specified wait time elapses + * before the element can be transferred. + * + * @throws NullPointerException if the specified element is null + */ + public boolean tryTransfer(E e, long timeout, TimeUnit unit) + throws InterruptedException { + if (xfer(e, true, TIMED, unit.toNanos(timeout)) == null) + return true; + if (!Thread.interrupted()) + return false; + throw new InterruptedException(); + } + + public E take() throws InterruptedException { + E e = xfer(null, false, SYNC, 0); + if (e != null) + return e; + Thread.interrupted(); + throw new InterruptedException(); + } + + public E poll(long timeout, TimeUnit unit) throws InterruptedException { + E e = xfer(null, false, TIMED, unit.toNanos(timeout)); + if (e != null || !Thread.interrupted()) + return e; + throw new InterruptedException(); + } + + public E poll() { + return xfer(null, false, NOW, 0); + } + + /** + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ + public int drainTo(Collection<? super E> c) { + if (c == null) + throw new NullPointerException(); + if (c == this) + throw new IllegalArgumentException(); + int n = 0; + for (E e; (e = poll()) != null;) { + c.add(e); + ++n; + } + return n; + } + + /** + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ + public int drainTo(Collection<? super E> c, int maxElements) { + if (c == null) + throw new NullPointerException(); + if (c == this) + throw new IllegalArgumentException(); + int n = 0; + for (E e; n < maxElements && (e = poll()) != null;) { + c.add(e); + ++n; + } + return n; + } + + /** + * Returns an iterator over the elements in this queue in proper sequence. + * The elements will be returned in order from first (head) to last (tail). + * + * <p>The returned iterator is a "weakly consistent" iterator that + * will never throw {@link java.util.ConcurrentModificationException + * ConcurrentModificationException}, and guarantees to traverse + * elements as they existed upon construction of the iterator, and + * may (but is not guaranteed to) reflect any modifications + * subsequent to construction. + * + * @return an iterator over the elements in this queue in proper sequence + */ + public Iterator<E> iterator() { + return new Itr(); + } + + public E peek() { + return firstDataItem(); + } + + /** + * Returns {@code true} if this queue contains no elements. + * + * @return {@code true} if this queue contains no elements + */ + public boolean isEmpty() { + for (Node p = head; p != null; p = succ(p)) { + if (!p.isMatched()) + return !p.isData; + } + return true; + } + + public boolean hasWaitingConsumer() { + return firstOfMode(false) != null; + } + + /** + * Returns the number of elements in this queue. If this queue + * contains more than {@code Integer.MAX_VALUE} elements, returns + * {@code Integer.MAX_VALUE}. + * + * <p>Beware that, unlike in most collections, this method is + * <em>NOT</em> a constant-time operation. Because of the + * asynchronous nature of these queues, determining the current + * number of elements requires an O(n) traversal. + * + * @return the number of elements in this queue + */ + public int size() { + return countOfMode(true); + } + + public int getWaitingConsumerCount() { + return countOfMode(false); + } + + /** + * Removes a single instance of the specified element from this queue, + * if it is present. More formally, removes an element {@code e} such + * that {@code o.equals(e)}, if this queue contains one or more such + * elements. + * Returns {@code true} if this queue contained the specified element + * (or equivalently, if this queue changed as a result of the call). + * + * @param o element to be removed from this queue, if present + * @return {@code true} if this queue changed as a result of the call + */ + public boolean remove(Object o) { + return findAndRemove(o); + } + + /** + * Returns {@code true} if this queue contains the specified element. + * More formally, returns {@code true} if and only if this queue contains + * at least one element {@code e} such that {@code o.equals(e)}. + * + * @param o object to be checked for containment in this queue + * @return {@code true} if this queue contains the specified element + */ + public boolean contains(Object o) { + if (o == null) return false; + for (Node p = head; p != null; p = succ(p)) { + Object item = p.item; + if (p.isData) { + if (item != null && item != p && o.equals(item)) + return true; + } + else if (item == null) + break; + } + return false; + } + + /** + * Always returns {@code Integer.MAX_VALUE} because a + * {@code LinkedTransferQueue} is not capacity constrained. + * + * @return {@code Integer.MAX_VALUE} (as specified by + * {@link java.util.concurrent.BlockingQueue#remainingCapacity() + * BlockingQueue.remainingCapacity}) + */ + public int remainingCapacity() { + return Integer.MAX_VALUE; + } + + /** + * Saves the state to a stream (that is, serializes it). + * + * @serialData All of the elements (each an {@code E}) in + * the proper order, followed by a null + * @param s the stream + */ + private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException { + s.defaultWriteObject(); + for (E e : this) + s.writeObject(e); + // Use trailing null as sentinel + s.writeObject(null); + } + + /** + * Reconstitutes the Queue instance from a stream (that is, + * deserializes it). + * + * @param s the stream + */ + private void readObject(java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + s.defaultReadObject(); + for (;;) { + @SuppressWarnings("unchecked") E item = (E) s.readObject(); + if (item == null) + break; + else + offer(item); + } + } + + // Unsafe mechanics + + private static final sun.misc.Unsafe UNSAFE; + private static final long headOffset; + private static final long tailOffset; + private static final long sweepVotesOffset; + static { + try { + UNSAFE = sun.misc.Unsafe.getUnsafe(); + Class<?> k = LinkedTransferQueue.class; + headOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("head")); + tailOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("tail")); + sweepVotesOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("sweepVotes")); + } catch (Exception e) { + throw new Error(e); + } + } +} diff --git a/luni/src/main/java/java/util/concurrent/Phaser.java b/luni/src/main/java/java/util/concurrent/Phaser.java new file mode 100644 index 0000000..25ff743 --- /dev/null +++ b/luni/src/main/java/java/util/concurrent/Phaser.java @@ -0,0 +1,1135 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +package java.util.concurrent; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.LockSupport; + +/** + * A reusable synchronization barrier, similar in functionality to + * {@link java.util.concurrent.CyclicBarrier CyclicBarrier} and + * {@link java.util.concurrent.CountDownLatch CountDownLatch} + * but supporting more flexible usage. + * + * <p> <b>Registration.</b> Unlike the case for other barriers, the + * number of parties <em>registered</em> to synchronize on a phaser + * may vary over time. Tasks may be registered at any time (using + * methods {@link #register}, {@link #bulkRegister}, or forms of + * constructors establishing initial numbers of parties), and + * optionally deregistered upon any arrival (using {@link + * #arriveAndDeregister}). As is the case with most basic + * synchronization constructs, registration and deregistration affect + * only internal counts; they do not establish any further internal + * bookkeeping, so tasks cannot query whether they are registered. + * (However, you can introduce such bookkeeping by subclassing this + * class.) + * + * <p> <b>Synchronization.</b> Like a {@code CyclicBarrier}, a {@code + * Phaser} may be repeatedly awaited. Method {@link + * #arriveAndAwaitAdvance} has effect analogous to {@link + * java.util.concurrent.CyclicBarrier#await CyclicBarrier.await}. Each + * generation of a phaser has an associated phase number. The phase + * number starts at zero, and advances when all parties arrive at the + * phaser, wrapping around to zero after reaching {@code + * Integer.MAX_VALUE}. The use of phase numbers enables independent + * control of actions upon arrival at a phaser and upon awaiting + * others, via two kinds of methods that may be invoked by any + * registered party: + * + * <ul> + * + * <li> <b>Arrival.</b> Methods {@link #arrive} and + * {@link #arriveAndDeregister} record arrival. These methods + * do not block, but return an associated <em>arrival phase + * number</em>; that is, the phase number of the phaser to which + * the arrival applied. When the final party for a given phase + * arrives, an optional action is performed and the phase + * advances. These actions are performed by the party + * triggering a phase advance, and are arranged by overriding + * method {@link #onAdvance(int, int)}, which also controls + * termination. Overriding this method is similar to, but more + * flexible than, providing a barrier action to a {@code + * CyclicBarrier}. + * + * <li> <b>Waiting.</b> Method {@link #awaitAdvance} requires an + * argument indicating an arrival phase number, and returns when + * the phaser advances to (or is already at) a different phase. + * Unlike similar constructions using {@code CyclicBarrier}, + * method {@code awaitAdvance} continues to wait even if the + * waiting thread is interrupted. Interruptible and timeout + * versions are also available, but exceptions encountered while + * tasks wait interruptibly or with timeout do not change the + * state of the phaser. If necessary, you can perform any + * associated recovery within handlers of those exceptions, + * often after invoking {@code forceTermination}. Phasers may + * also be used by tasks executing in a {@link ForkJoinPool}, + * which will ensure sufficient parallelism to execute tasks + * when others are blocked waiting for a phase to advance. + * + * </ul> + * + * <p> <b>Termination.</b> A phaser may enter a <em>termination</em> + * state, that may be checked using method {@link #isTerminated}. Upon + * termination, all synchronization methods immediately return without + * waiting for advance, as indicated by a negative return value. + * Similarly, attempts to register upon termination have no effect. + * Termination is triggered when an invocation of {@code onAdvance} + * returns {@code true}. The default implementation returns {@code + * true} if a deregistration has caused the number of registered + * parties to become zero. As illustrated below, when phasers control + * actions with a fixed number of iterations, it is often convenient + * to override this method to cause termination when the current phase + * number reaches a threshold. Method {@link #forceTermination} is + * also available to abruptly release waiting threads and allow them + * to terminate. + * + * <p> <b>Tiering.</b> Phasers may be <em>tiered</em> (i.e., + * constructed in tree structures) to reduce contention. Phasers with + * large numbers of parties that would otherwise experience heavy + * synchronization contention costs may instead be set up so that + * groups of sub-phasers share a common parent. This may greatly + * increase throughput even though it incurs greater per-operation + * overhead. + * + * <p>In a tree of tiered phasers, registration and deregistration of + * child phasers with their parent are managed automatically. + * Whenever the number of registered parties of a child phaser becomes + * non-zero (as established in the {@link #Phaser(Phaser,int)} + * constructor, {@link #register}, or {@link #bulkRegister}), the + * child phaser is registered with its parent. Whenever the number of + * registered parties becomes zero as the result of an invocation of + * {@link #arriveAndDeregister}, the child phaser is deregistered + * from its parent. + * + * <p><b>Monitoring.</b> While synchronization methods may be invoked + * only by registered parties, the current state of a phaser may be + * monitored by any caller. At any given moment there are {@link + * #getRegisteredParties} parties in total, of which {@link + * #getArrivedParties} have arrived at the current phase ({@link + * #getPhase}). When the remaining ({@link #getUnarrivedParties}) + * parties arrive, the phase advances. The values returned by these + * methods may reflect transient states and so are not in general + * useful for synchronization control. Method {@link #toString} + * returns snapshots of these state queries in a form convenient for + * informal monitoring. + * + * <p><b>Sample usages:</b> + * + * <p>A {@code Phaser} may be used instead of a {@code CountDownLatch} + * to control a one-shot action serving a variable number of parties. + * The typical idiom is for the method setting this up to first + * register, then start the actions, then deregister, as in: + * + * <pre> {@code + * void runTasks(List<Runnable> tasks) { + * final Phaser phaser = new Phaser(1); // "1" to register self + * // create and start threads + * for (final Runnable task : tasks) { + * phaser.register(); + * new Thread() { + * public void run() { + * phaser.arriveAndAwaitAdvance(); // await all creation + * task.run(); + * } + * }.start(); + * } + * + * // allow threads to start and deregister self + * phaser.arriveAndDeregister(); + * }}</pre> + * + * <p>One way to cause a set of threads to repeatedly perform actions + * for a given number of iterations is to override {@code onAdvance}: + * + * <pre> {@code + * void startTasks(List<Runnable> tasks, final int iterations) { + * final Phaser phaser = new Phaser() { + * protected boolean onAdvance(int phase, int registeredParties) { + * return phase >= iterations || registeredParties == 0; + * } + * }; + * phaser.register(); + * for (final Runnable task : tasks) { + * phaser.register(); + * new Thread() { + * public void run() { + * do { + * task.run(); + * phaser.arriveAndAwaitAdvance(); + * } while (!phaser.isTerminated()); + * } + * }.start(); + * } + * phaser.arriveAndDeregister(); // deregister self, don't wait + * }}</pre> + * + * If the main task must later await termination, it + * may re-register and then execute a similar loop: + * <pre> {@code + * // ... + * phaser.register(); + * while (!phaser.isTerminated()) + * phaser.arriveAndAwaitAdvance();}</pre> + * + * <p>Related constructions may be used to await particular phase numbers + * in contexts where you are sure that the phase will never wrap around + * {@code Integer.MAX_VALUE}. For example: + * + * <pre> {@code + * void awaitPhase(Phaser phaser, int phase) { + * int p = phaser.register(); // assumes caller not already registered + * while (p < phase) { + * if (phaser.isTerminated()) + * // ... deal with unexpected termination + * else + * p = phaser.arriveAndAwaitAdvance(); + * } + * phaser.arriveAndDeregister(); + * }}</pre> + * + * + * <p>To create a set of {@code n} tasks using a tree of phasers, you + * could use code of the following form, assuming a Task class with a + * constructor accepting a {@code Phaser} that it registers with upon + * construction. After invocation of {@code build(new Task[n], 0, n, + * new Phaser())}, these tasks could then be started, for example by + * submitting to a pool: + * + * <pre> {@code + * void build(Task[] tasks, int lo, int hi, Phaser ph) { + * if (hi - lo > TASKS_PER_PHASER) { + * for (int i = lo; i < hi; i += TASKS_PER_PHASER) { + * int j = Math.min(i + TASKS_PER_PHASER, hi); + * build(tasks, i, j, new Phaser(ph)); + * } + * } else { + * for (int i = lo; i < hi; ++i) + * tasks[i] = new Task(ph); + * // assumes new Task(ph) performs ph.register() + * } + * }}</pre> + * + * The best value of {@code TASKS_PER_PHASER} depends mainly on + * expected synchronization rates. A value as low as four may + * be appropriate for extremely small per-phase task bodies (thus + * high rates), or up to hundreds for extremely large ones. + * + * <p><b>Implementation notes</b>: This implementation restricts the + * maximum number of parties to 65535. Attempts to register additional + * parties result in {@code IllegalStateException}. However, you can and + * should create tiered phasers to accommodate arbitrarily large sets + * of participants. + * + * @since 1.7 + * @hide + * @author Doug Lea + */ +public class Phaser { + /* + * This class implements an extension of X10 "clocks". Thanks to + * Vijay Saraswat for the idea, and to Vivek Sarkar for + * enhancements to extend functionality. + */ + + /** + * Primary state representation, holding four bit-fields: + * + * unarrived -- the number of parties yet to hit barrier (bits 0-15) + * parties -- the number of parties to wait (bits 16-31) + * phase -- the generation of the barrier (bits 32-62) + * terminated -- set if barrier is terminated (bit 63 / sign) + * + * Except that a phaser with no registered parties is + * distinguished by the otherwise illegal state of having zero + * parties and one unarrived parties (encoded as EMPTY below). + * + * To efficiently maintain atomicity, these values are packed into + * a single (atomic) long. Good performance relies on keeping + * state decoding and encoding simple, and keeping race windows + * short. + * + * All state updates are performed via CAS except initial + * registration of a sub-phaser (i.e., one with a non-null + * parent). In this (relatively rare) case, we use built-in + * synchronization to lock while first registering with its + * parent. + * + * The phase of a subphaser is allowed to lag that of its + * ancestors until it is actually accessed -- see method + * reconcileState. + */ + private volatile long state; + + private static final int MAX_PARTIES = 0xffff; + private static final int MAX_PHASE = Integer.MAX_VALUE; + private static final int PARTIES_SHIFT = 16; + private static final int PHASE_SHIFT = 32; + private static final int UNARRIVED_MASK = 0xffff; // to mask ints + private static final long PARTIES_MASK = 0xffff0000L; // to mask longs + private static final long COUNTS_MASK = 0xffffffffL; + private static final long TERMINATION_BIT = 1L << 63; + + // some special values + private static final int ONE_ARRIVAL = 1; + private static final int ONE_PARTY = 1 << PARTIES_SHIFT; + private static final int ONE_DEREGISTER = ONE_ARRIVAL|ONE_PARTY; + private static final int EMPTY = 1; + + // The following unpacking methods are usually manually inlined + + private static int unarrivedOf(long s) { + int counts = (int)s; + return (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK); + } + + private static int partiesOf(long s) { + return (int)s >>> PARTIES_SHIFT; + } + + private static int phaseOf(long s) { + return (int)(s >>> PHASE_SHIFT); + } + + private static int arrivedOf(long s) { + int counts = (int)s; + return (counts == EMPTY) ? 0 : + (counts >>> PARTIES_SHIFT) - (counts & UNARRIVED_MASK); + } + + /** + * The parent of this phaser, or null if none + */ + private final Phaser parent; + + /** + * The root of phaser tree. Equals this if not in a tree. + */ + private final Phaser root; + + /** + * Heads of Treiber stacks for waiting threads. To eliminate + * contention when releasing some threads while adding others, we + * use two of them, alternating across even and odd phases. + * Subphasers share queues with root to speed up releases. + */ + private final AtomicReference<QNode> evenQ; + private final AtomicReference<QNode> oddQ; + + private AtomicReference<QNode> queueFor(int phase) { + return ((phase & 1) == 0) ? evenQ : oddQ; + } + + /** + * Returns message string for bounds exceptions on arrival. + */ + private String badArrive(long s) { + return "Attempted arrival of unregistered party for " + + stateToString(s); + } + + /** + * Returns message string for bounds exceptions on registration. + */ + private String badRegister(long s) { + return "Attempt to register more than " + + MAX_PARTIES + " parties for " + stateToString(s); + } + + /** + * Main implementation for methods arrive and arriveAndDeregister. + * Manually tuned to speed up and minimize race windows for the + * common case of just decrementing unarrived field. + * + * @param adjust value to subtract from state; + * ONE_ARRIVAL for arrive, + * ONE_DEREGISTER for arriveAndDeregister + */ + private int doArrive(int adjust) { + final Phaser root = this.root; + for (;;) { + long s = (root == this) ? state : reconcileState(); + int phase = (int)(s >>> PHASE_SHIFT); + if (phase < 0) + return phase; + int counts = (int)s; + int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK); + if (unarrived <= 0) + throw new IllegalStateException(badArrive(s)); + if (UNSAFE.compareAndSwapLong(this, stateOffset, s, s-=adjust)) { + if (unarrived == 1) { + long n = s & PARTIES_MASK; // base of next state + int nextUnarrived = (int)n >>> PARTIES_SHIFT; + if (root == this) { + if (onAdvance(phase, nextUnarrived)) + n |= TERMINATION_BIT; + else if (nextUnarrived == 0) + n |= EMPTY; + else + n |= nextUnarrived; + int nextPhase = (phase + 1) & MAX_PHASE; + n |= (long)nextPhase << PHASE_SHIFT; + UNSAFE.compareAndSwapLong(this, stateOffset, s, n); + releaseWaiters(phase); + } + else if (nextUnarrived == 0) { // propagate deregistration + phase = parent.doArrive(ONE_DEREGISTER); + UNSAFE.compareAndSwapLong(this, stateOffset, + s, s | EMPTY); + } + else + phase = parent.doArrive(ONE_ARRIVAL); + } + return phase; + } + } + } + + /** + * Implementation of register, bulkRegister + * + * @param registrations number to add to both parties and + * unarrived fields. Must be greater than zero. + */ + private int doRegister(int registrations) { + // adjustment to state + long adjust = ((long)registrations << PARTIES_SHIFT) | registrations; + final Phaser parent = this.parent; + int phase; + for (;;) { + long s = (parent == null) ? state : reconcileState(); + int counts = (int)s; + int parties = counts >>> PARTIES_SHIFT; + int unarrived = counts & UNARRIVED_MASK; + if (registrations > MAX_PARTIES - parties) + throw new IllegalStateException(badRegister(s)); + phase = (int)(s >>> PHASE_SHIFT); + if (phase < 0) + break; + if (counts != EMPTY) { // not 1st registration + if (parent == null || reconcileState() == s) { + if (unarrived == 0) // wait out advance + root.internalAwaitAdvance(phase, null); + else if (UNSAFE.compareAndSwapLong(this, stateOffset, + s, s + adjust)) + break; + } + } + else if (parent == null) { // 1st root registration + long next = ((long)phase << PHASE_SHIFT) | adjust; + if (UNSAFE.compareAndSwapLong(this, stateOffset, s, next)) + break; + } + else { + synchronized (this) { // 1st sub registration + if (state == s) { // recheck under lock + phase = parent.doRegister(1); + if (phase < 0) + break; + // finish registration whenever parent registration + // succeeded, even when racing with termination, + // since these are part of the same "transaction". + while (!UNSAFE.compareAndSwapLong + (this, stateOffset, s, + ((long)phase << PHASE_SHIFT) | adjust)) { + s = state; + phase = (int)(root.state >>> PHASE_SHIFT); + // assert (int)s == EMPTY; + } + break; + } + } + } + } + return phase; + } + + /** + * Resolves lagged phase propagation from root if necessary. + * Reconciliation normally occurs when root has advanced but + * subphasers have not yet done so, in which case they must finish + * their own advance by setting unarrived to parties (or if + * parties is zero, resetting to unregistered EMPTY state). + * + * @return reconciled state + */ + private long reconcileState() { + final Phaser root = this.root; + long s = state; + if (root != this) { + int phase, p; + // CAS to root phase with current parties, tripping unarrived + while ((phase = (int)(root.state >>> PHASE_SHIFT)) != + (int)(s >>> PHASE_SHIFT) && + !UNSAFE.compareAndSwapLong + (this, stateOffset, s, + s = (((long)phase << PHASE_SHIFT) | + ((phase < 0) ? (s & COUNTS_MASK) : + (((p = (int)s >>> PARTIES_SHIFT) == 0) ? EMPTY : + ((s & PARTIES_MASK) | p)))))) + s = state; + } + return s; + } + + /** + * Creates a new phaser with no initially registered parties, no + * parent, and initial phase number 0. Any thread using this + * phaser will need to first register for it. + */ + public Phaser() { + this(null, 0); + } + + /** + * Creates a new phaser with the given number of registered + * unarrived parties, no parent, and initial phase number 0. + * + * @param parties the number of parties required to advance to the + * next phase + * @throws IllegalArgumentException if parties less than zero + * or greater than the maximum number of parties supported + */ + public Phaser(int parties) { + this(null, parties); + } + + /** + * Equivalent to {@link #Phaser(Phaser, int) Phaser(parent, 0)}. + * + * @param parent the parent phaser + */ + public Phaser(Phaser parent) { + this(parent, 0); + } + + /** + * Creates a new phaser with the given parent and number of + * registered unarrived parties. When the given parent is non-null + * and the given number of parties is greater than zero, this + * child phaser is registered with its parent. + * + * @param parent the parent phaser + * @param parties the number of parties required to advance to the + * next phase + * @throws IllegalArgumentException if parties less than zero + * or greater than the maximum number of parties supported + */ + public Phaser(Phaser parent, int parties) { + if (parties >>> PARTIES_SHIFT != 0) + throw new IllegalArgumentException("Illegal number of parties"); + int phase = 0; + this.parent = parent; + if (parent != null) { + final Phaser root = parent.root; + this.root = root; + this.evenQ = root.evenQ; + this.oddQ = root.oddQ; + if (parties != 0) + phase = parent.doRegister(1); + } + else { + this.root = this; + this.evenQ = new AtomicReference<QNode>(); + this.oddQ = new AtomicReference<QNode>(); + } + this.state = (parties == 0) ? (long)EMPTY : + ((long)phase << PHASE_SHIFT) | + ((long)parties << PARTIES_SHIFT) | + ((long)parties); + } + + /** + * Adds a new unarrived party to this phaser. If an ongoing + * invocation of {@link #onAdvance} is in progress, this method + * may await its completion before returning. If this phaser has + * a parent, and this phaser previously had no registered parties, + * this child phaser is also registered with its parent. If + * this phaser is terminated, the attempt to register has + * no effect, and a negative value is returned. + * + * @return the arrival phase number to which this registration + * applied. If this value is negative, then this phaser has + * terminated, in which case registration has no effect. + * @throws IllegalStateException if attempting to register more + * than the maximum supported number of parties + */ + public int register() { + return doRegister(1); + } + + /** + * Adds the given number of new unarrived parties to this phaser. + * If an ongoing invocation of {@link #onAdvance} is in progress, + * this method may await its completion before returning. If this + * phaser has a parent, and the given number of parties is greater + * than zero, and this phaser previously had no registered + * parties, this child phaser is also registered with its parent. + * If this phaser is terminated, the attempt to register has no + * effect, and a negative value is returned. + * + * @param parties the number of additional parties required to + * advance to the next phase + * @return the arrival phase number to which this registration + * applied. If this value is negative, then this phaser has + * terminated, in which case registration has no effect. + * @throws IllegalStateException if attempting to register more + * than the maximum supported number of parties + * @throws IllegalArgumentException if {@code parties < 0} + */ + public int bulkRegister(int parties) { + if (parties < 0) + throw new IllegalArgumentException(); + if (parties == 0) + return getPhase(); + return doRegister(parties); + } + + /** + * Arrives at this phaser, without waiting for others to arrive. + * + * <p>It is a usage error for an unregistered party to invoke this + * method. However, this error may result in an {@code + * IllegalStateException} only upon some subsequent operation on + * this phaser, if ever. + * + * @return the arrival phase number, or a negative value if terminated + * @throws IllegalStateException if not terminated and the number + * of unarrived parties would become negative + */ + public int arrive() { + return doArrive(ONE_ARRIVAL); + } + + /** + * Arrives at this phaser and deregisters from it without waiting + * for others to arrive. Deregistration reduces the number of + * parties required to advance in future phases. If this phaser + * has a parent, and deregistration causes this phaser to have + * zero parties, this phaser is also deregistered from its parent. + * + * <p>It is a usage error for an unregistered party to invoke this + * method. However, this error may result in an {@code + * IllegalStateException} only upon some subsequent operation on + * this phaser, if ever. + * + * @return the arrival phase number, or a negative value if terminated + * @throws IllegalStateException if not terminated and the number + * of registered or unarrived parties would become negative + */ + public int arriveAndDeregister() { + return doArrive(ONE_DEREGISTER); + } + + /** + * Arrives at this phaser and awaits others. Equivalent in effect + * to {@code awaitAdvance(arrive())}. If you need to await with + * interruption or timeout, you can arrange this with an analogous + * construction using one of the other forms of the {@code + * awaitAdvance} method. If instead you need to deregister upon + * arrival, use {@code awaitAdvance(arriveAndDeregister())}. + * + * <p>It is a usage error for an unregistered party to invoke this + * method. However, this error may result in an {@code + * IllegalStateException} only upon some subsequent operation on + * this phaser, if ever. + * + * @return the arrival phase number, or the (negative) + * {@linkplain #getPhase() current phase} if terminated + * @throws IllegalStateException if not terminated and the number + * of unarrived parties would become negative + */ + public int arriveAndAwaitAdvance() { + // Specialization of doArrive+awaitAdvance eliminating some reads/paths + final Phaser root = this.root; + for (;;) { + long s = (root == this) ? state : reconcileState(); + int phase = (int)(s >>> PHASE_SHIFT); + if (phase < 0) + return phase; + int counts = (int)s; + int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK); + if (unarrived <= 0) + throw new IllegalStateException(badArrive(s)); + if (UNSAFE.compareAndSwapLong(this, stateOffset, s, + s -= ONE_ARRIVAL)) { + if (unarrived > 1) + return root.internalAwaitAdvance(phase, null); + if (root != this) + return parent.arriveAndAwaitAdvance(); + long n = s & PARTIES_MASK; // base of next state + int nextUnarrived = (int)n >>> PARTIES_SHIFT; + if (onAdvance(phase, nextUnarrived)) + n |= TERMINATION_BIT; + else if (nextUnarrived == 0) + n |= EMPTY; + else + n |= nextUnarrived; + int nextPhase = (phase + 1) & MAX_PHASE; + n |= (long)nextPhase << PHASE_SHIFT; + if (!UNSAFE.compareAndSwapLong(this, stateOffset, s, n)) + return (int)(state >>> PHASE_SHIFT); // terminated + releaseWaiters(phase); + return nextPhase; + } + } + } + + /** + * Awaits the phase of this phaser to advance from the given phase + * value, returning immediately if the current phase is not equal + * to the given phase value or this phaser is terminated. + * + * @param phase an arrival phase number, or negative value if + * terminated; this argument is normally the value returned by a + * previous call to {@code arrive} or {@code arriveAndDeregister}. + * @return the next arrival phase number, or the argument if it is + * negative, or the (negative) {@linkplain #getPhase() current phase} + * if terminated + */ + public int awaitAdvance(int phase) { + final Phaser root = this.root; + long s = (root == this) ? state : reconcileState(); + int p = (int)(s >>> PHASE_SHIFT); + if (phase < 0) + return phase; + if (p == phase) + return root.internalAwaitAdvance(phase, null); + return p; + } + + /** + * Awaits the phase of this phaser to advance from the given phase + * value, throwing {@code InterruptedException} if interrupted + * while waiting, or returning immediately if the current phase is + * not equal to the given phase value or this phaser is + * terminated. + * + * @param phase an arrival phase number, or negative value if + * terminated; this argument is normally the value returned by a + * previous call to {@code arrive} or {@code arriveAndDeregister}. + * @return the next arrival phase number, or the argument if it is + * negative, or the (negative) {@linkplain #getPhase() current phase} + * if terminated + * @throws InterruptedException if thread interrupted while waiting + */ + public int awaitAdvanceInterruptibly(int phase) + throws InterruptedException { + final Phaser root = this.root; + long s = (root == this) ? state : reconcileState(); + int p = (int)(s >>> PHASE_SHIFT); + if (phase < 0) + return phase; + if (p == phase) { + QNode node = new QNode(this, phase, true, false, 0L); + p = root.internalAwaitAdvance(phase, node); + if (node.wasInterrupted) + throw new InterruptedException(); + } + return p; + } + + /** + * Awaits the phase of this phaser to advance from the given phase + * value or the given timeout to elapse, throwing {@code + * InterruptedException} if interrupted while waiting, or + * returning immediately if the current phase is not equal to the + * given phase value or this phaser is terminated. + * + * @param phase an arrival phase number, or negative value if + * terminated; this argument is normally the value returned by a + * previous call to {@code arrive} or {@code arriveAndDeregister}. + * @param timeout how long to wait before giving up, in units of + * {@code unit} + * @param unit a {@code TimeUnit} determining how to interpret the + * {@code timeout} parameter + * @return the next arrival phase number, or the argument if it is + * negative, or the (negative) {@linkplain #getPhase() current phase} + * if terminated + * @throws InterruptedException if thread interrupted while waiting + * @throws TimeoutException if timed out while waiting + */ + public int awaitAdvanceInterruptibly(int phase, + long timeout, TimeUnit unit) + throws InterruptedException, TimeoutException { + long nanos = unit.toNanos(timeout); + final Phaser root = this.root; + long s = (root == this) ? state : reconcileState(); + int p = (int)(s >>> PHASE_SHIFT); + if (phase < 0) + return phase; + if (p == phase) { + QNode node = new QNode(this, phase, true, true, nanos); + p = root.internalAwaitAdvance(phase, node); + if (node.wasInterrupted) + throw new InterruptedException(); + else if (p == phase) + throw new TimeoutException(); + } + return p; + } + + /** + * Forces this phaser to enter termination state. Counts of + * registered parties are unaffected. If this phaser is a member + * of a tiered set of phasers, then all of the phasers in the set + * are terminated. If this phaser is already terminated, this + * method has no effect. This method may be useful for + * coordinating recovery after one or more tasks encounter + * unexpected exceptions. + */ + public void forceTermination() { + // Only need to change root state + final Phaser root = this.root; + long s; + while ((s = root.state) >= 0) { + if (UNSAFE.compareAndSwapLong(root, stateOffset, + s, s | TERMINATION_BIT)) { + // signal all threads + releaseWaiters(0); // Waiters on evenQ + releaseWaiters(1); // Waiters on oddQ + return; + } + } + } + + /** + * Returns the current phase number. The maximum phase number is + * {@code Integer.MAX_VALUE}, after which it restarts at + * zero. Upon termination, the phase number is negative, + * in which case the prevailing phase prior to termination + * may be obtained via {@code getPhase() + Integer.MIN_VALUE}. + * + * @return the phase number, or a negative value if terminated + */ + public final int getPhase() { + return (int)(root.state >>> PHASE_SHIFT); + } + + /** + * Returns the number of parties registered at this phaser. + * + * @return the number of parties + */ + public int getRegisteredParties() { + return partiesOf(state); + } + + /** + * Returns the number of registered parties that have arrived at + * the current phase of this phaser. If this phaser has terminated, + * the returned value is meaningless and arbitrary. + * + * @return the number of arrived parties + */ + public int getArrivedParties() { + return arrivedOf(reconcileState()); + } + + /** + * Returns the number of registered parties that have not yet + * arrived at the current phase of this phaser. If this phaser has + * terminated, the returned value is meaningless and arbitrary. + * + * @return the number of unarrived parties + */ + public int getUnarrivedParties() { + return unarrivedOf(reconcileState()); + } + + /** + * Returns the parent of this phaser, or {@code null} if none. + * + * @return the parent of this phaser, or {@code null} if none + */ + public Phaser getParent() { + return parent; + } + + /** + * Returns the root ancestor of this phaser, which is the same as + * this phaser if it has no parent. + * + * @return the root ancestor of this phaser + */ + public Phaser getRoot() { + return root; + } + + /** + * Returns {@code true} if this phaser has been terminated. + * + * @return {@code true} if this phaser has been terminated + */ + public boolean isTerminated() { + return root.state < 0L; + } + + /** + * Overridable method to perform an action upon impending phase + * advance, and to control termination. This method is invoked + * upon arrival of the party advancing this phaser (when all other + * waiting parties are dormant). If this method returns {@code + * true}, this phaser will be set to a final termination state + * upon advance, and subsequent calls to {@link #isTerminated} + * will return true. Any (unchecked) Exception or Error thrown by + * an invocation of this method is propagated to the party + * attempting to advance this phaser, in which case no advance + * occurs. + * + * <p>The arguments to this method provide the state of the phaser + * prevailing for the current transition. The effects of invoking + * arrival, registration, and waiting methods on this phaser from + * within {@code onAdvance} are unspecified and should not be + * relied on. + * + * <p>If this phaser is a member of a tiered set of phasers, then + * {@code onAdvance} is invoked only for its root phaser on each + * advance. + * + * <p>To support the most common use cases, the default + * implementation of this method returns {@code true} when the + * number of registered parties has become zero as the result of a + * party invoking {@code arriveAndDeregister}. You can disable + * this behavior, thus enabling continuation upon future + * registrations, by overriding this method to always return + * {@code false}: + * + * <pre> {@code + * Phaser phaser = new Phaser() { + * protected boolean onAdvance(int phase, int parties) { return false; } + * }}</pre> + * + * @param phase the current phase number on entry to this method, + * before this phaser is advanced + * @param registeredParties the current number of registered parties + * @return {@code true} if this phaser should terminate + */ + protected boolean onAdvance(int phase, int registeredParties) { + return registeredParties == 0; + } + + /** + * Returns a string identifying this phaser, as well as its + * state. The state, in brackets, includes the String {@code + * "phase = "} followed by the phase number, {@code "parties = "} + * followed by the number of registered parties, and {@code + * "arrived = "} followed by the number of arrived parties. + * + * @return a string identifying this phaser, as well as its state + */ + public String toString() { + return stateToString(reconcileState()); + } + + /** + * Implementation of toString and string-based error messages + */ + private String stateToString(long s) { + return super.toString() + + "[phase = " + phaseOf(s) + + " parties = " + partiesOf(s) + + " arrived = " + arrivedOf(s) + "]"; + } + + // Waiting mechanics + + /** + * Removes and signals threads from queue for phase. + */ + private void releaseWaiters(int phase) { + QNode q; // first element of queue + Thread t; // its thread + AtomicReference<QNode> head = (phase & 1) == 0 ? evenQ : oddQ; + while ((q = head.get()) != null && + q.phase != (int)(root.state >>> PHASE_SHIFT)) { + if (head.compareAndSet(q, q.next) && + (t = q.thread) != null) { + q.thread = null; + LockSupport.unpark(t); + } + } + } + + /** + * Variant of releaseWaiters that additionally tries to remove any + * nodes no longer waiting for advance due to timeout or + * interrupt. Currently, nodes are removed only if they are at + * head of queue, which suffices to reduce memory footprint in + * most usages. + * + * @return current phase on exit + */ + private int abortWait(int phase) { + AtomicReference<QNode> head = (phase & 1) == 0 ? evenQ : oddQ; + for (;;) { + Thread t; + QNode q = head.get(); + int p = (int)(root.state >>> PHASE_SHIFT); + if (q == null || ((t = q.thread) != null && q.phase == p)) + return p; + if (head.compareAndSet(q, q.next) && t != null) { + q.thread = null; + LockSupport.unpark(t); + } + } + } + + /** The number of CPUs, for spin control */ + private static final int NCPU = Runtime.getRuntime().availableProcessors(); + + /** + * The number of times to spin before blocking while waiting for + * advance, per arrival while waiting. On multiprocessors, fully + * blocking and waking up a large number of threads all at once is + * usually a very slow process, so we use rechargeable spins to + * avoid it when threads regularly arrive: When a thread in + * internalAwaitAdvance notices another arrival before blocking, + * and there appear to be enough CPUs available, it spins + * SPINS_PER_ARRIVAL more times before blocking. The value trades + * off good-citizenship vs big unnecessary slowdowns. + */ + static final int SPINS_PER_ARRIVAL = (NCPU < 2) ? 1 : 1 << 8; + + /** + * Possibly blocks and waits for phase to advance unless aborted. + * Call only on root phaser. + * + * @param phase current phase + * @param node if non-null, the wait node to track interrupt and timeout; + * if null, denotes noninterruptible wait + * @return current phase + */ + private int internalAwaitAdvance(int phase, QNode node) { + // assert root == this; + releaseWaiters(phase-1); // ensure old queue clean + boolean queued = false; // true when node is enqueued + int lastUnarrived = 0; // to increase spins upon change + int spins = SPINS_PER_ARRIVAL; + long s; + int p; + while ((p = (int)((s = state) >>> PHASE_SHIFT)) == phase) { + if (node == null) { // spinning in noninterruptible mode + int unarrived = (int)s & UNARRIVED_MASK; + if (unarrived != lastUnarrived && + (lastUnarrived = unarrived) < NCPU) + spins += SPINS_PER_ARRIVAL; + boolean interrupted = Thread.interrupted(); + if (interrupted || --spins < 0) { // need node to record intr + node = new QNode(this, phase, false, false, 0L); + node.wasInterrupted = interrupted; + } + } + else if (node.isReleasable()) // done or aborted + break; + else if (!queued) { // push onto queue + AtomicReference<QNode> head = (phase & 1) == 0 ? evenQ : oddQ; + QNode q = node.next = head.get(); + if ((q == null || q.phase == phase) && + (int)(state >>> PHASE_SHIFT) == phase) // avoid stale enq + queued = head.compareAndSet(q, node); + } + else { + try { + ForkJoinPool.managedBlock(node); + } catch (InterruptedException ie) { + node.wasInterrupted = true; + } + } + } + + if (node != null) { + if (node.thread != null) + node.thread = null; // avoid need for unpark() + if (node.wasInterrupted && !node.interruptible) + Thread.currentThread().interrupt(); + if (p == phase && (p = (int)(state >>> PHASE_SHIFT)) == phase) + return abortWait(phase); // possibly clean up on abort + } + releaseWaiters(phase); + return p; + } + + /** + * Wait nodes for Treiber stack representing wait queue + */ + static final class QNode implements ForkJoinPool.ManagedBlocker { + final Phaser phaser; + final int phase; + final boolean interruptible; + final boolean timed; + boolean wasInterrupted; + long nanos; + long lastTime; + volatile Thread thread; // nulled to cancel wait + QNode next; + + QNode(Phaser phaser, int phase, boolean interruptible, + boolean timed, long nanos) { + this.phaser = phaser; + this.phase = phase; + this.interruptible = interruptible; + this.nanos = nanos; + this.timed = timed; + this.lastTime = timed ? System.nanoTime() : 0L; + thread = Thread.currentThread(); + } + + public boolean isReleasable() { + if (thread == null) + return true; + if (phaser.getPhase() != phase) { + thread = null; + return true; + } + if (Thread.interrupted()) + wasInterrupted = true; + if (wasInterrupted && interruptible) { + thread = null; + return true; + } + if (timed) { + if (nanos > 0L) { + long now = System.nanoTime(); + nanos -= now - lastTime; + lastTime = now; + } + if (nanos <= 0L) { + thread = null; + return true; + } + } + return false; + } + + public boolean block() { + if (isReleasable()) + return true; + else if (!timed) + LockSupport.park(this); + else if (nanos > 0) + LockSupport.parkNanos(this, nanos); + return isReleasable(); + } + } + + // Unsafe mechanics + + private static final sun.misc.Unsafe UNSAFE; + private static final long stateOffset; + static { + try { + UNSAFE = sun.misc.Unsafe.getUnsafe(); + Class<?> k = Phaser.class; + stateOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("state")); + } catch (Exception e) { + throw new Error(e); + } + } +} diff --git a/luni/src/main/java/java/util/concurrent/PriorityBlockingQueue.java b/luni/src/main/java/java/util/concurrent/PriorityBlockingQueue.java index cffbe64..26c72eb 100644 --- a/luni/src/main/java/java/util/concurrent/PriorityBlockingQueue.java +++ b/luni/src/main/java/java/util/concurrent/PriorityBlockingQueue.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; @@ -81,7 +81,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> * java.util.PriorityQueue operations within a lock, as was done * in a previous version of this class. To maintain * interoperability, a plain PriorityQueue is still used during - * serialization, which maintains compatibility at the espense of + * serialization, which maintains compatibility at the expense of * transiently doubling overhead. */ @@ -139,7 +139,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> * to maintain compatibility with previous versions * of this class. Non-null only during serialization/deserialization. */ - private PriorityQueue q; + private PriorityQueue<E> q; /** * Creates a {@code PriorityBlockingQueue} with the default @@ -278,14 +278,13 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> /** * Mechanics for poll(). Call only while holding lock. */ - private E extract() { - E result; + private E dequeue() { int n = size - 1; if (n < 0) - result = null; + return null; else { Object[] array = queue; - result = (E) array[0]; + E result = (E) array[0]; E x = (E) array[n]; array[n] = null; Comparator<? super E> cmp = comparator; @@ -294,8 +293,8 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> else siftDownUsingComparator(0, x, array, n, cmp); size = n; + return result; } - return result; } /** @@ -312,6 +311,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> * @param k the position to fill * @param x the item to insert * @param array the heap array + * @param n heap size */ private static <T> void siftUpComparable(int k, T x, Object[] array) { Comparable<? super T> key = (Comparable<? super T>) x; @@ -476,7 +476,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> * @param timeout This parameter is ignored as the method never blocks * @param unit This parameter is ignored as the method never blocks * @return {@code true} (as specified by - * {@link BlockingQueue#offer BlockingQueue.offer}) + * {@link BlockingQueue#offer(Object,long,TimeUnit) BlockingQueue.offer}) * @throws ClassCastException if the specified element cannot be compared * with elements currently in the priority queue according to the * priority queue's ordering @@ -489,13 +489,11 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> public E poll() { final ReentrantLock lock = this.lock; lock.lock(); - E result; try { - result = extract(); + return dequeue(); } finally { lock.unlock(); } - return result; } public E take() throws InterruptedException { @@ -503,7 +501,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> lock.lockInterruptibly(); E result; try { - while ( (result = extract()) == null) + while ( (result = dequeue()) == null) notEmpty.await(); } finally { lock.unlock(); @@ -517,7 +515,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> lock.lockInterruptibly(); E result; try { - while ( (result = extract()) == null && nanos > 0) + while ( (result = dequeue()) == null && nanos > 0) nanos = notEmpty.awaitNanos(nanos); } finally { lock.unlock(); @@ -528,13 +526,11 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> public E peek() { final ReentrantLock lock = this.lock; lock.lock(); - E result; try { - result = size > 0 ? (E) queue[0] : null; + return (size == 0) ? null : (E) queue[0]; } finally { lock.unlock(); } - return result; } /** @@ -618,32 +614,28 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> * @return {@code true} if this queue changed as a result of the call */ public boolean remove(Object o) { - boolean removed = false; final ReentrantLock lock = this.lock; lock.lock(); try { int i = indexOf(o); - if (i != -1) { - removeAt(i); - removed = true; - } + if (i == -1) + return false; + removeAt(i); + return true; } finally { lock.unlock(); } - return removed; } - /** * Identity-based version for use in Itr.remove */ - private void removeEQ(Object o) { + void removeEQ(Object o) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] array = queue; - int n = size; - for (int i = 0; i < n; i++) { + for (int i = 0, n = size; i < n; i++) { if (o == array[i]) { removeAt(i); break; @@ -663,15 +655,13 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> * @return {@code true} if this queue contains the specified element */ public boolean contains(Object o) { - int index; final ReentrantLock lock = this.lock; lock.lock(); try { - index = indexOf(o); + return indexOf(o) != -1; } finally { lock.unlock(); } - return index != -1; } /** @@ -697,7 +687,6 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> } } - public String toString() { final ReentrantLock lock = this.lock; lock.lock(); @@ -708,7 +697,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> StringBuilder sb = new StringBuilder(); sb.append('['); for (int i = 0; i < n; ++i) { - E e = (E)queue[i]; + Object e = queue[i]; sb.append(e == this ? "(this Collection)" : e); if (i != n - 1) sb.append(',').append(' '); @@ -726,23 +715,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> * @throws IllegalArgumentException {@inheritDoc} */ public int drainTo(Collection<? super E> c) { - if (c == null) - throw new NullPointerException(); - if (c == this) - throw new IllegalArgumentException(); - final ReentrantLock lock = this.lock; - lock.lock(); - try { - int n = 0; - E e; - while ( (e = extract()) != null) { - c.add(e); - ++n; - } - return n; - } finally { - lock.unlock(); - } + return drainTo(c, Integer.MAX_VALUE); } /** @@ -761,11 +734,10 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> final ReentrantLock lock = this.lock; lock.lock(); try { - int n = 0; - E e; - while (n < maxElements && (e = extract()) != null) { - c.add(e); - ++n; + int n = Math.min(size, maxElements); + for (int i = 0; i < n; i++) { + c.add((E) queue[0]); // In this order, in case add() throws. + dequeue(); } return n; } finally { @@ -813,8 +785,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> * The following code can be used to dump the queue into a newly * allocated array of {@code String}: * - * <pre> - * String[] y = x.toArray(new String[0]);</pre> + * <pre> {@code String[] y = x.toArray(new String[0]);}</pre> * * Note that {@code toArray(new Object[0])} is identical in function to * {@code toArray()}. @@ -867,7 +838,7 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> */ final class Itr implements Iterator<E> { final Object[] array; // Array of all elements - int cursor; // index of next element to return; + int cursor; // index of next element to return int lastRet; // index of last element, or -1 if no such Itr(Object[] array) { @@ -904,8 +875,8 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> throws java.io.IOException { lock.lock(); try { - int n = size; // avoid zero capacity argument - q = new PriorityQueue<E>(n == 0 ? 1 : n, comparator); + // avoid zero capacity argument + q = new PriorityQueue<E>(Math.max(size, 1), comparator); q.addAll(this); s.defaultWriteObject(); } finally { @@ -933,21 +904,16 @@ public class PriorityBlockingQueue<E> extends AbstractQueue<E> } // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); - private static final long allocationSpinLockOffset = - objectFieldOffset(UNSAFE, "allocationSpinLock", - PriorityBlockingQueue.class); - - static long objectFieldOffset(sun.misc.Unsafe UNSAFE, - String field, Class<?> klazz) { + private static final sun.misc.Unsafe UNSAFE; + private static final long allocationSpinLockOffset; + static { try { - return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field)); - } catch (NoSuchFieldException e) { - // Convert Exception to corresponding Error - NoSuchFieldError error = new NoSuchFieldError(field); - error.initCause(e); - throw error; + UNSAFE = sun.misc.Unsafe.getUnsafe(); + Class<?> k = PriorityBlockingQueue.class; + allocationSpinLockOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("allocationSpinLock")); + } catch (Exception e) { + throw new Error(e); } } - } diff --git a/luni/src/main/java/java/util/concurrent/RecursiveAction.java b/luni/src/main/java/java/util/concurrent/RecursiveAction.java new file mode 100644 index 0000000..48066c9 --- /dev/null +++ b/luni/src/main/java/java/util/concurrent/RecursiveAction.java @@ -0,0 +1,165 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +package java.util.concurrent; + +/** + * A recursive resultless {@link ForkJoinTask}. This class + * establishes conventions to parameterize resultless actions as + * {@code Void} {@code ForkJoinTask}s. Because {@code null} is the + * only valid value of type {@code Void}, methods such as {@code join} + * always return {@code null} upon completion. + * + * <p><b>Sample Usages.</b> Here is a simple but complete ForkJoin + * sort that sorts a given {@code long[]} array: + * + * <pre> {@code + * static class SortTask extends RecursiveAction { + * final long[] array; final int lo, hi; + * SortTask(long[] array, int lo, int hi) { + * this.array = array; this.lo = lo; this.hi = hi; + * } + * SortTask(long[] array) { this(array, 0, array.length); } + * protected void compute() { + * if (hi - lo < THRESHOLD) + * sortSequentially(lo, hi); + * else { + * int mid = (lo + hi) >>> 1; + * invokeAll(new SortTask(array, lo, mid), + * new SortTask(array, mid, hi)); + * merge(lo, mid, hi); + * } + * } + * // implementation details follow: + * final static int THRESHOLD = 1000; + * void sortSequentially(int lo, int hi) { + * Arrays.sort(array, lo, hi); + * } + * void merge(int lo, int mid, int hi) { + * long[] buf = Arrays.copyOfRange(array, lo, mid); + * for (int i = 0, j = lo, k = mid; i < buf.length; j++) + * array[j] = (k == hi || buf[i] < array[k]) ? + * buf[i++] : array[k++]; + * } + * }}</pre> + * + * You could then sort {@code anArray} by creating {@code new + * SortTask(anArray)} and invoking it in a ForkJoinPool. As a more + * concrete simple example, the following task increments each element + * of an array: + * <pre> {@code + * class IncrementTask extends RecursiveAction { + * final long[] array; final int lo, hi; + * IncrementTask(long[] array, int lo, int hi) { + * this.array = array; this.lo = lo; this.hi = hi; + * } + * protected void compute() { + * if (hi - lo < THRESHOLD) { + * for (int i = lo; i < hi; ++i) + * array[i]++; + * } + * else { + * int mid = (lo + hi) >>> 1; + * invokeAll(new IncrementTask(array, lo, mid), + * new IncrementTask(array, mid, hi)); + * } + * } + * }}</pre> + * + * <p>The following example illustrates some refinements and idioms + * that may lead to better performance: RecursiveActions need not be + * fully recursive, so long as they maintain the basic + * divide-and-conquer approach. Here is a class that sums the squares + * of each element of a double array, by subdividing out only the + * right-hand-sides of repeated divisions by two, and keeping track of + * them with a chain of {@code next} references. It uses a dynamic + * threshold based on method {@code getSurplusQueuedTaskCount}, but + * counterbalances potential excess partitioning by directly + * performing leaf actions on unstolen tasks rather than further + * subdividing. + * + * <pre> {@code + * double sumOfSquares(ForkJoinPool pool, double[] array) { + * int n = array.length; + * Applyer a = new Applyer(array, 0, n, null); + * pool.invoke(a); + * return a.result; + * } + * + * class Applyer extends RecursiveAction { + * final double[] array; + * final int lo, hi; + * double result; + * Applyer next; // keeps track of right-hand-side tasks + * Applyer(double[] array, int lo, int hi, Applyer next) { + * this.array = array; this.lo = lo; this.hi = hi; + * this.next = next; + * } + * + * double atLeaf(int l, int h) { + * double sum = 0; + * for (int i = l; i < h; ++i) // perform leftmost base step + * sum += array[i] * array[i]; + * return sum; + * } + * + * protected void compute() { + * int l = lo; + * int h = hi; + * Applyer right = null; + * while (h - l > 1 && getSurplusQueuedTaskCount() <= 3) { + * int mid = (l + h) >>> 1; + * right = new Applyer(array, mid, h, right); + * right.fork(); + * h = mid; + * } + * double sum = atLeaf(l, h); + * while (right != null) { + * if (right.tryUnfork()) // directly calculate if not stolen + * sum += right.atLeaf(right.lo, right.hi); + * else { + * right.join(); + * sum += right.result; + * } + * right = right.next; + * } + * result = sum; + * } + * }}</pre> + * + * @since 1.7 + * @hide + * @author Doug Lea + */ +public abstract class RecursiveAction extends ForkJoinTask<Void> { + private static final long serialVersionUID = 5232453952276485070L; + + /** + * The main computation performed by this task. + */ + protected abstract void compute(); + + /** + * Always returns {@code null}. + * + * @return {@code null} always + */ + public final Void getRawResult() { return null; } + + /** + * Requires null completion value. + */ + protected final void setRawResult(Void mustBeNull) { } + + /** + * Implements execution conventions for RecursiveActions. + */ + protected final boolean exec() { + compute(); + return true; + } + +} diff --git a/luni/src/main/java/java/util/concurrent/RecursiveTask.java b/luni/src/main/java/java/util/concurrent/RecursiveTask.java new file mode 100644 index 0000000..5e17280 --- /dev/null +++ b/luni/src/main/java/java/util/concurrent/RecursiveTask.java @@ -0,0 +1,69 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +package java.util.concurrent; + +/** + * A recursive result-bearing {@link ForkJoinTask}. + * + * <p>For a classic example, here is a task computing Fibonacci numbers: + * + * <pre> {@code + * class Fibonacci extends RecursiveTask<Integer> { + * final int n; + * Fibonacci(int n) { this.n = n; } + * Integer compute() { + * if (n <= 1) + * return n; + * Fibonacci f1 = new Fibonacci(n - 1); + * f1.fork(); + * Fibonacci f2 = new Fibonacci(n - 2); + * return f2.compute() + f1.join(); + * } + * }}</pre> + * + * However, besides being a dumb way to compute Fibonacci functions + * (there is a simple fast linear algorithm that you'd use in + * practice), this is likely to perform poorly because the smallest + * subtasks are too small to be worthwhile splitting up. Instead, as + * is the case for nearly all fork/join applications, you'd pick some + * minimum granularity size (for example 10 here) for which you always + * sequentially solve rather than subdividing. + * + * @since 1.7 + * @hide + * @author Doug Lea + */ +public abstract class RecursiveTask<V> extends ForkJoinTask<V> { + private static final long serialVersionUID = 5232453952276485270L; + + /** + * The result of the computation. + */ + V result; + + /** + * The main computation performed by this task. + */ + protected abstract V compute(); + + public final V getRawResult() { + return result; + } + + protected final void setRawResult(V value) { + result = value; + } + + /** + * Implements execution conventions for RecursiveTask. + */ + protected final boolean exec() { + result = compute(); + return true; + } + +} diff --git a/luni/src/main/java/java/util/concurrent/RejectedExecutionException.java b/luni/src/main/java/java/util/concurrent/RejectedExecutionException.java index 30b043d..f0005d1 100644 --- a/luni/src/main/java/java/util/concurrent/RejectedExecutionException.java +++ b/luni/src/main/java/java/util/concurrent/RejectedExecutionException.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; @@ -49,8 +49,8 @@ public class RejectedExecutionException extends RuntimeException { /** * Constructs a <tt>RejectedExecutionException</tt> with the - * specified cause. The detail message is set to: <pre> (cause == - * null ? null : cause.toString())</pre> (which typically contains + * specified cause. The detail message is set to {@code (cause == + * null ? null : cause.toString())} (which typically contains * the class and detail message of <tt>cause</tt>). * * @param cause the cause (which is saved for later retrieval by the diff --git a/luni/src/main/java/java/util/concurrent/RejectedExecutionHandler.java b/luni/src/main/java/java/util/concurrent/RejectedExecutionHandler.java index 417a27c..8c000ea 100644 --- a/luni/src/main/java/java/util/concurrent/RejectedExecutionHandler.java +++ b/luni/src/main/java/java/util/concurrent/RejectedExecutionHandler.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; diff --git a/luni/src/main/java/java/util/concurrent/RunnableFuture.java b/luni/src/main/java/java/util/concurrent/RunnableFuture.java index d74211d..2d6d52c 100644 --- a/luni/src/main/java/java/util/concurrent/RunnableFuture.java +++ b/luni/src/main/java/java/util/concurrent/RunnableFuture.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; diff --git a/luni/src/main/java/java/util/concurrent/RunnableScheduledFuture.java b/luni/src/main/java/java/util/concurrent/RunnableScheduledFuture.java index 0e8cc32..fbb995c 100644 --- a/luni/src/main/java/java/util/concurrent/RunnableScheduledFuture.java +++ b/luni/src/main/java/java/util/concurrent/RunnableScheduledFuture.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; diff --git a/luni/src/main/java/java/util/concurrent/ScheduledExecutorService.java b/luni/src/main/java/java/util/concurrent/ScheduledExecutorService.java index 6cb4e27..71e57ed 100644 --- a/luni/src/main/java/java/util/concurrent/ScheduledExecutorService.java +++ b/luni/src/main/java/java/util/concurrent/ScheduledExecutorService.java @@ -1,12 +1,10 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; -import java.util.concurrent.atomic.*; -import java.util.*; /** * An {@link ExecutorService} that can schedule commands to run after a given diff --git a/luni/src/main/java/java/util/concurrent/ScheduledFuture.java b/luni/src/main/java/java/util/concurrent/ScheduledFuture.java index 239d681..3745cb0 100644 --- a/luni/src/main/java/java/util/concurrent/ScheduledFuture.java +++ b/luni/src/main/java/java/util/concurrent/ScheduledFuture.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; diff --git a/luni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java b/luni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java index c2eaedf..e41f0c3 100644 --- a/luni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java +++ b/luni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java @@ -1,16 +1,19 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; -import java.util.concurrent.atomic.*; -import java.util.concurrent.locks.*; +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; import java.util.*; // BEGIN android-note -// Omit class-level docs on setRemoveOnCancelPolicy() +// omit class-level docs on setRemoveOnCancelPolicy() +// removed security manager docs // END android-note /** @@ -138,7 +141,7 @@ public class ScheduledThreadPoolExecutor * Sequence number to break scheduling ties, and in turn to * guarantee FIFO order among tied entries. */ - private static final AtomicLong sequencer = new AtomicLong(0); + private static final AtomicLong sequencer = new AtomicLong(); /** * Returns current nanosecond time. @@ -203,11 +206,11 @@ public class ScheduledThreadPoolExecutor } public long getDelay(TimeUnit unit) { - return unit.convert(time - now(), TimeUnit.NANOSECONDS); + return unit.convert(time - now(), NANOSECONDS); } public int compareTo(Delayed other) { - if (other == this) // compare zero ONLY if same object + if (other == this) // compare zero if same object return 0; if (other instanceof ScheduledFutureTask) { ScheduledFutureTask<?> x = (ScheduledFutureTask<?>)other; @@ -221,9 +224,9 @@ public class ScheduledThreadPoolExecutor else return 1; } - long d = (getDelay(TimeUnit.NANOSECONDS) - - other.getDelay(TimeUnit.NANOSECONDS)); - return (d == 0) ? 0 : ((d < 0) ? -1 : 1); + long diff = (getDelay(NANOSECONDS) - + other.getDelay(NANOSECONDS)); + return (diff < 0) ? -1 : (diff > 0) ? 1 : 0; } /** @@ -302,7 +305,7 @@ public class ScheduledThreadPoolExecutor remove(task)) task.cancel(false); else - prestartCoreThread(); + ensurePrestart(); } } @@ -318,7 +321,7 @@ public class ScheduledThreadPoolExecutor if (!canRunInCurrentRunState(true) && remove(task)) task.cancel(false); else - prestartCoreThread(); + ensurePrestart(); } } @@ -396,7 +399,7 @@ public class ScheduledThreadPoolExecutor * @throws IllegalArgumentException if {@code corePoolSize < 0} */ public ScheduledThreadPoolExecutor(int corePoolSize) { - super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS, + super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue()); } @@ -413,7 +416,7 @@ public class ScheduledThreadPoolExecutor */ public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory) { - super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS, + super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue(), threadFactory); } @@ -430,7 +433,7 @@ public class ScheduledThreadPoolExecutor */ public ScheduledThreadPoolExecutor(int corePoolSize, RejectedExecutionHandler handler) { - super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS, + super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue(), handler); } @@ -451,7 +454,7 @@ public class ScheduledThreadPoolExecutor public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory, RejectedExecutionHandler handler) { - super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS, + super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue(), threadFactory, handler); } @@ -480,7 +483,7 @@ public class ScheduledThreadPoolExecutor private long overflowFree(long delay) { Delayed head = (Delayed) super.getQueue().peek(); if (head != null) { - long headDelay = head.getDelay(TimeUnit.NANOSECONDS); + long headDelay = head.getDelay(NANOSECONDS); if (headDelay < 0 && (delay - headDelay < 0)) delay = Long.MAX_VALUE + headDelay; } @@ -588,7 +591,7 @@ public class ScheduledThreadPoolExecutor * @throws NullPointerException {@inheritDoc} */ public void execute(Runnable command) { - schedule(command, 0, TimeUnit.NANOSECONDS); + schedule(command, 0, NANOSECONDS); } // Override AbstractExecutorService methods @@ -598,7 +601,7 @@ public class ScheduledThreadPoolExecutor * @throws NullPointerException {@inheritDoc} */ public Future<?> submit(Runnable task) { - return schedule(task, 0, TimeUnit.NANOSECONDS); + return schedule(task, 0, NANOSECONDS); } /** @@ -606,8 +609,7 @@ public class ScheduledThreadPoolExecutor * @throws NullPointerException {@inheritDoc} */ public <T> Future<T> submit(Runnable task, T result) { - return schedule(Executors.callable(task, result), - 0, TimeUnit.NANOSECONDS); + return schedule(Executors.callable(task, result), 0, NANOSECONDS); } /** @@ -615,7 +617,7 @@ public class ScheduledThreadPoolExecutor * @throws NullPointerException {@inheritDoc} */ public <T> Future<T> submit(Callable<T> task) { - return schedule(task, 0, TimeUnit.NANOSECONDS); + return schedule(task, 0, NANOSECONDS); } /** @@ -690,8 +692,9 @@ public class ScheduledThreadPoolExecutor * @param value if {@code true}, remove on cancellation, else don't * @see #getRemoveOnCancelPolicy * @since 1.7 + * @hide */ - /*public*/ void setRemoveOnCancelPolicy(boolean value) { // android-changed + public void setRemoveOnCancelPolicy(boolean value) { removeOnCancel = value; } @@ -704,8 +707,9 @@ public class ScheduledThreadPoolExecutor * from the queue * @see #setRemoveOnCancelPolicy * @since 1.7 + * @hide */ - /*public*/ boolean getRemoveOnCancelPolicy() { // android-changed + public boolean getRemoveOnCancelPolicy() { return removeOnCancel; } @@ -724,8 +728,6 @@ public class ScheduledThreadPoolExecutor * ContinueExistingPeriodicTasksAfterShutdownPolicy} has been set * {@code true}, future executions of existing periodic tasks will * be cancelled. - * - * @throws SecurityException {@inheritDoc} */ public void shutdown() { super.shutdown(); @@ -750,7 +752,6 @@ public class ScheduledThreadPoolExecutor * including those tasks submitted using {@code execute}, * which are for scheduling purposes used as the basis of a * zero-delay {@code ScheduledFuture}. - * @throws SecurityException {@inheritDoc} */ public List<Runnable> shutdownNow() { return super.shutdownNow(); @@ -803,8 +804,8 @@ public class ScheduledThreadPoolExecutor */ private static final int INITIAL_CAPACITY = 16; - private RunnableScheduledFuture[] queue = - new RunnableScheduledFuture[INITIAL_CAPACITY]; + private RunnableScheduledFuture<?>[] queue = + new RunnableScheduledFuture<?>[INITIAL_CAPACITY]; private final ReentrantLock lock = new ReentrantLock(); private int size = 0; @@ -835,7 +836,7 @@ public class ScheduledThreadPoolExecutor /** * Set f's heapIndex if it is a ScheduledFutureTask. */ - private void setIndex(RunnableScheduledFuture f, int idx) { + private void setIndex(RunnableScheduledFuture<?> f, int idx) { if (f instanceof ScheduledFutureTask) ((ScheduledFutureTask)f).heapIndex = idx; } @@ -844,10 +845,10 @@ public class ScheduledThreadPoolExecutor * Sift element added at bottom up to its heap-ordered spot. * Call only when holding lock. */ - private void siftUp(int k, RunnableScheduledFuture key) { + private void siftUp(int k, RunnableScheduledFuture<?> key) { while (k > 0) { int parent = (k - 1) >>> 1; - RunnableScheduledFuture e = queue[parent]; + RunnableScheduledFuture<?> e = queue[parent]; if (key.compareTo(e) >= 0) break; queue[k] = e; @@ -862,11 +863,11 @@ public class ScheduledThreadPoolExecutor * Sift element added at top down to its heap-ordered spot. * Call only when holding lock. */ - private void siftDown(int k, RunnableScheduledFuture key) { + private void siftDown(int k, RunnableScheduledFuture<?> key) { int half = size >>> 1; while (k < half) { int child = (k << 1) + 1; - RunnableScheduledFuture c = queue[child]; + RunnableScheduledFuture<?> c = queue[child]; int right = child + 1; if (right < size && c.compareTo(queue[right]) > 0) c = queue[child = right]; @@ -931,7 +932,7 @@ public class ScheduledThreadPoolExecutor setIndex(queue[i], -1); int s = --size; - RunnableScheduledFuture replacement = queue[s]; + RunnableScheduledFuture<?> replacement = queue[s]; queue[s] = null; if (s != i) { siftDown(i, replacement); @@ -962,7 +963,7 @@ public class ScheduledThreadPoolExecutor return Integer.MAX_VALUE; } - public RunnableScheduledFuture peek() { + public RunnableScheduledFuture<?> peek() { final ReentrantLock lock = this.lock; lock.lock(); try { @@ -975,7 +976,7 @@ public class ScheduledThreadPoolExecutor public boolean offer(Runnable x) { if (x == null) throw new NullPointerException(); - RunnableScheduledFuture e = (RunnableScheduledFuture)x; + RunnableScheduledFuture<?> e = (RunnableScheduledFuture<?>)x; final ReentrantLock lock = this.lock; lock.lock(); try { @@ -1017,9 +1018,9 @@ public class ScheduledThreadPoolExecutor * holding lock. * @param f the task to remove and return */ - private RunnableScheduledFuture finishPoll(RunnableScheduledFuture f) { + private RunnableScheduledFuture<?> finishPoll(RunnableScheduledFuture<?> f) { int s = --size; - RunnableScheduledFuture x = queue[s]; + RunnableScheduledFuture<?> x = queue[s]; queue[s] = null; if (s != 0) siftDown(0, x); @@ -1027,12 +1028,12 @@ public class ScheduledThreadPoolExecutor return f; } - public RunnableScheduledFuture poll() { + public RunnableScheduledFuture<?> poll() { final ReentrantLock lock = this.lock; lock.lock(); try { - RunnableScheduledFuture first = queue[0]; - if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0) + RunnableScheduledFuture<?> first = queue[0]; + if (first == null || first.getDelay(NANOSECONDS) > 0) return null; else return finishPoll(first); @@ -1041,16 +1042,16 @@ public class ScheduledThreadPoolExecutor } } - public RunnableScheduledFuture take() throws InterruptedException { + public RunnableScheduledFuture<?> take() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { for (;;) { - RunnableScheduledFuture first = queue[0]; + RunnableScheduledFuture<?> first = queue[0]; if (first == null) available.await(); else { - long delay = first.getDelay(TimeUnit.NANOSECONDS); + long delay = first.getDelay(NANOSECONDS); if (delay <= 0) return finishPoll(first); else if (leader != null) @@ -1074,21 +1075,21 @@ public class ScheduledThreadPoolExecutor } } - public RunnableScheduledFuture poll(long timeout, TimeUnit unit) + public RunnableScheduledFuture<?> poll(long timeout, TimeUnit unit) throws InterruptedException { long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { for (;;) { - RunnableScheduledFuture first = queue[0]; + RunnableScheduledFuture<?> first = queue[0]; if (first == null) { if (nanos <= 0) return null; else nanos = available.awaitNanos(nanos); } else { - long delay = first.getDelay(TimeUnit.NANOSECONDS); + long delay = first.getDelay(NANOSECONDS); if (delay <= 0) return finishPoll(first); if (nanos <= 0) @@ -1120,7 +1121,7 @@ public class ScheduledThreadPoolExecutor lock.lock(); try { for (int i = 0; i < size; i++) { - RunnableScheduledFuture t = queue[i]; + RunnableScheduledFuture<?> t = queue[i]; if (t != null) { queue[i] = null; setIndex(t, -1); @@ -1133,14 +1134,14 @@ public class ScheduledThreadPoolExecutor } /** - * Return and remove first element only if it is expired. + * Return first element only if it is expired. * Used only by drainTo. Call only when holding lock. */ - private RunnableScheduledFuture pollExpired() { - RunnableScheduledFuture first = queue[0]; - if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0) - return null; - return finishPoll(first); + private RunnableScheduledFuture<?> peekExpired() { + // assert lock.isHeldByCurrentThread(); + RunnableScheduledFuture<?> first = queue[0]; + return (first == null || first.getDelay(NANOSECONDS) > 0) ? + null : first; } public int drainTo(Collection<? super Runnable> c) { @@ -1151,10 +1152,11 @@ public class ScheduledThreadPoolExecutor final ReentrantLock lock = this.lock; lock.lock(); try { - RunnableScheduledFuture first; + RunnableScheduledFuture<?> first; int n = 0; - while ((first = pollExpired()) != null) { - c.add(first); + while ((first = peekExpired()) != null) { + c.add(first); // In this order, in case add() throws. + finishPoll(first); ++n; } return n; @@ -1173,10 +1175,11 @@ public class ScheduledThreadPoolExecutor final ReentrantLock lock = this.lock; lock.lock(); try { - RunnableScheduledFuture first; + RunnableScheduledFuture<?> first; int n = 0; - while (n < maxElements && (first = pollExpired()) != null) { - c.add(first); + while (n < maxElements && (first = peekExpired()) != null) { + c.add(first); // In this order, in case add() throws. + finishPoll(first); ++n; } return n; diff --git a/luni/src/main/java/java/util/concurrent/Semaphore.java b/luni/src/main/java/java/util/concurrent/Semaphore.java index 62dea1a..bf2524c 100644 --- a/luni/src/main/java/java/util/concurrent/Semaphore.java +++ b/luni/src/main/java/java/util/concurrent/Semaphore.java @@ -1,13 +1,12 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; import java.util.*; import java.util.concurrent.locks.*; -import java.util.concurrent.atomic.*; /** * A counting semaphore. Conceptually, a semaphore maintains a set of @@ -20,7 +19,7 @@ import java.util.concurrent.atomic.*; * <p>Semaphores are often used to restrict the number of threads than can * access some (physical or logical) resource. For example, here is * a class that uses a semaphore to control access to a pool of items: - * <pre> + * <pre> {@code * class Pool { * private static final int MAX_AVAILABLE = 100; * private final Semaphore available = new Semaphore(MAX_AVAILABLE, true); @@ -62,9 +61,7 @@ import java.util.concurrent.atomic.*; * } * return false; * } - * - * } - * </pre> + * }}</pre> * * <p>Before obtaining an item each thread must acquire a permit from * the semaphore, guaranteeing that an item is available for use. When diff --git a/luni/src/main/java/java/util/concurrent/SynchronousQueue.java b/luni/src/main/java/java/util/concurrent/SynchronousQueue.java index 51d40c0..b05ae0a 100644 --- a/luni/src/main/java/java/util/concurrent/SynchronousQueue.java +++ b/luni/src/main/java/java/util/concurrent/SynchronousQueue.java @@ -2,13 +2,12 @@ * Written by Doug Lea, Bill Scherer, and Michael Scott with * assistance from members of JCP JSR-166 Expert Group and released to * the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; import java.util.concurrent.locks.*; import java.util.*; -import libcore.util.EmptyArray; // BEGIN android-note // removed link to collections framework docs @@ -250,12 +249,22 @@ public class SynchronousQueue<E> extends AbstractQueue<E> } // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); - private static final long nextOffset = - objectFieldOffset(UNSAFE, "next", SNode.class); - private static final long matchOffset = - objectFieldOffset(UNSAFE, "match", SNode.class); - + private static final sun.misc.Unsafe UNSAFE; + private static final long matchOffset; + private static final long nextOffset; + + static { + try { + UNSAFE = sun.misc.Unsafe.getUnsafe(); + Class<?> k = SNode.class; + matchOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("match")); + nextOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("next")); + } catch (Exception e) { + throw new Error(e); + } + } } /** The head (top) of the stack */ @@ -393,7 +402,6 @@ public class SynchronousQueue<E> extends AbstractQueue<E> */ long lastTime = timed ? System.nanoTime() : 0; Thread w = Thread.currentThread(); - SNode h = head; int spins = (shouldSpin(s) ? (timed ? maxTimedSpins : maxUntimedSpins) : 0); for (;;) { @@ -469,10 +477,18 @@ public class SynchronousQueue<E> extends AbstractQueue<E> } // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); - private static final long headOffset = - objectFieldOffset(UNSAFE, "head", TransferStack.class); - + private static final sun.misc.Unsafe UNSAFE; + private static final long headOffset; + static { + try { + UNSAFE = sun.misc.Unsafe.getUnsafe(); + Class<?> k = TransferStack.class; + headOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("head")); + } catch (Exception e) { + throw new Error(e); + } + } } /** Dual Queue */ @@ -529,11 +545,22 @@ public class SynchronousQueue<E> extends AbstractQueue<E> } // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); - private static final long nextOffset = - objectFieldOffset(UNSAFE, "next", QNode.class); - private static final long itemOffset = - objectFieldOffset(UNSAFE, "item", QNode.class); + private static final sun.misc.Unsafe UNSAFE; + private static final long itemOffset; + private static final long nextOffset; + + static { + try { + UNSAFE = sun.misc.Unsafe.getUnsafe(); + Class<?> k = QNode.class; + itemOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("item")); + nextOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("next")); + } catch (Exception e) { + throw new Error(e); + } + } } /** Head of queue */ @@ -762,15 +789,24 @@ public class SynchronousQueue<E> extends AbstractQueue<E> } } - // unsafe mechanics - private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); - private static final long headOffset = - objectFieldOffset(UNSAFE, "head", TransferQueue.class); - private static final long tailOffset = - objectFieldOffset(UNSAFE, "tail", TransferQueue.class); - private static final long cleanMeOffset = - objectFieldOffset(UNSAFE, "cleanMe", TransferQueue.class); - + private static final sun.misc.Unsafe UNSAFE; + private static final long headOffset; + private static final long tailOffset; + private static final long cleanMeOffset; + static { + try { + UNSAFE = sun.misc.Unsafe.getUnsafe(); + Class<?> k = TransferQueue.class; + headOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("head")); + tailOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("tail")); + cleanMeOffset = UNSAFE.objectFieldOffset + (k.getDeclaredField("cleanMe")); + } catch (Exception e) { + throw new Error(e); + } + } } /** @@ -998,8 +1034,19 @@ public class SynchronousQueue<E> extends AbstractQueue<E> * * @return an empty iterator */ + @SuppressWarnings("unchecked") public Iterator<E> iterator() { - return Collections.<E>emptySet().iterator(); // android-changed + return (Iterator<E>) EmptyIterator.EMPTY_ITERATOR; + } + + // Replicated from a previous version of Collections + private static class EmptyIterator<E> implements Iterator<E> { + static final EmptyIterator<Object> EMPTY_ITERATOR + = new EmptyIterator<Object>(); + + public boolean hasNext() { return false; } + public E next() { throw new NoSuchElementException(); } + public void remove() { throw new IllegalStateException(); } } /** @@ -1007,7 +1054,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E> * @return a zero-length array */ public Object[] toArray() { - return EmptyArray.OBJECT; // android-changed + return new Object[0]; } /** @@ -1036,8 +1083,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E> if (c == this) throw new IllegalArgumentException(); int n = 0; - E e; - while ( (e = poll()) != null) { + for (E e; (e = poll()) != null;) { c.add(e); ++n; } @@ -1056,8 +1102,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E> if (c == this) throw new IllegalArgumentException(); int n = 0; - E e; - while (n < maxElements && (e = poll()) != null) { + for (E e; n < maxElements && (e = poll()) != null;) { c.add(e); ++n; } @@ -1084,7 +1129,7 @@ public class SynchronousQueue<E> extends AbstractQueue<E> private WaitQueue waitingConsumers; /** - * Save the state to a stream (that is, serialize it). + * Saves the state to a stream (that is, serializes it). * * @param s the stream */ diff --git a/luni/src/main/java/java/util/concurrent/ThreadFactory.java b/luni/src/main/java/java/util/concurrent/ThreadFactory.java index 2f0fb1a..d1a4eb6 100644 --- a/luni/src/main/java/java/util/concurrent/ThreadFactory.java +++ b/luni/src/main/java/java/util/concurrent/ThreadFactory.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; @@ -13,13 +13,12 @@ package java.util.concurrent; * * <p> * The simplest implementation of this interface is just: - * <pre> + * <pre> {@code * class SimpleThreadFactory implements ThreadFactory { * public Thread newThread(Runnable r) { * return new Thread(r); * } - * } - * </pre> + * }}</pre> * * The {@link Executors#defaultThreadFactory} method provides a more * useful simple implementation, that sets the created thread context diff --git a/luni/src/main/java/java/util/concurrent/ThreadLocalRandom.java b/luni/src/main/java/java/util/concurrent/ThreadLocalRandom.java new file mode 100644 index 0000000..a559321 --- /dev/null +++ b/luni/src/main/java/java/util/concurrent/ThreadLocalRandom.java @@ -0,0 +1,198 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +package java.util.concurrent; + +import java.util.Random; + +/** + * A random number generator isolated to the current thread. Like the + * global {@link java.util.Random} generator used by the {@link + * java.lang.Math} class, a {@code ThreadLocalRandom} is initialized + * with an internally generated seed that may not otherwise be + * modified. When applicable, use of {@code ThreadLocalRandom} rather + * than shared {@code Random} objects in concurrent programs will + * typically encounter much less overhead and contention. Use of + * {@code ThreadLocalRandom} is particularly appropriate when multiple + * tasks (for example, each a {@link ForkJoinTask}) use random numbers + * in parallel in thread pools. + * + * <p>Usages of this class should typically be of the form: + * {@code ThreadLocalRandom.current().nextX(...)} (where + * {@code X} is {@code Int}, {@code Long}, etc). + * When all usages are of this form, it is never possible to + * accidently share a {@code ThreadLocalRandom} across multiple threads. + * + * <p>This class also provides additional commonly used bounded random + * generation methods. + * + * @since 1.7 + * @hide + * @author Doug Lea + */ +public class ThreadLocalRandom extends Random { + // same constants as Random, but must be redeclared because private + private static final long multiplier = 0x5DEECE66DL; + private static final long addend = 0xBL; + private static final long mask = (1L << 48) - 1; + + /** + * The random seed. We can't use super.seed. + */ + private long rnd; + + /** + * Initialization flag to permit calls to setSeed to succeed only + * while executing the Random constructor. We can't allow others + * since it would cause setting seed in one part of a program to + * unintentionally impact other usages by the thread. + */ + boolean initialized; + + // Padding to help avoid memory contention among seed updates in + // different TLRs in the common case that they are located near + // each other. + private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7; + + /** + * The actual ThreadLocal + */ + private static final ThreadLocal<ThreadLocalRandom> localRandom = + new ThreadLocal<ThreadLocalRandom>() { + protected ThreadLocalRandom initialValue() { + return new ThreadLocalRandom(); + } + }; + + + /** + * Constructor called only by localRandom.initialValue. + */ + ThreadLocalRandom() { + super(); + initialized = true; + } + + /** + * Returns the current thread's {@code ThreadLocalRandom}. + * + * @return the current thread's {@code ThreadLocalRandom} + */ + public static ThreadLocalRandom current() { + return localRandom.get(); + } + + /** + * Throws {@code UnsupportedOperationException}. Setting seeds in + * this generator is not supported. + * + * @throws UnsupportedOperationException always + */ + public void setSeed(long seed) { + if (initialized) + throw new UnsupportedOperationException(); + rnd = (seed ^ multiplier) & mask; + } + + protected int next(int bits) { + rnd = (rnd * multiplier + addend) & mask; + return (int) (rnd >>> (48-bits)); + } + + /** + * Returns a pseudorandom, uniformly distributed value between the + * given least value (inclusive) and bound (exclusive). + * + * @param least the least value returned + * @param bound the upper bound (exclusive) + * @throws IllegalArgumentException if least greater than or equal + * to bound + * @return the next value + */ + public int nextInt(int least, int bound) { + if (least >= bound) + throw new IllegalArgumentException(); + return nextInt(bound - least) + least; + } + + /** + * Returns a pseudorandom, uniformly distributed value + * between 0 (inclusive) and the specified value (exclusive). + * + * @param n the bound on the random number to be returned. Must be + * positive. + * @return the next value + * @throws IllegalArgumentException if n is not positive + */ + public long nextLong(long n) { + if (n <= 0) + throw new IllegalArgumentException("n must be positive"); + // Divide n by two until small enough for nextInt. On each + // iteration (at most 31 of them but usually much less), + // randomly choose both whether to include high bit in result + // (offset) and whether to continue with the lower vs upper + // half (which makes a difference only if odd). + long offset = 0; + while (n >= Integer.MAX_VALUE) { + int bits = next(2); + long half = n >>> 1; + long nextn = ((bits & 2) == 0) ? half : n - half; + if ((bits & 1) == 0) + offset += n - nextn; + n = nextn; + } + return offset + nextInt((int) n); + } + + /** + * Returns a pseudorandom, uniformly distributed value between the + * given least value (inclusive) and bound (exclusive). + * + * @param least the least value returned + * @param bound the upper bound (exclusive) + * @return the next value + * @throws IllegalArgumentException if least greater than or equal + * to bound + */ + public long nextLong(long least, long bound) { + if (least >= bound) + throw new IllegalArgumentException(); + return nextLong(bound - least) + least; + } + + /** + * Returns a pseudorandom, uniformly distributed {@code double} value + * between 0 (inclusive) and the specified value (exclusive). + * + * @param n the bound on the random number to be returned. Must be + * positive. + * @return the next value + * @throws IllegalArgumentException if n is not positive + */ + public double nextDouble(double n) { + if (n <= 0) + throw new IllegalArgumentException("n must be positive"); + return nextDouble() * n; + } + + /** + * Returns a pseudorandom, uniformly distributed value between the + * given least value (inclusive) and bound (exclusive). + * + * @param least the least value returned + * @param bound the upper bound (exclusive) + * @return the next value + * @throws IllegalArgumentException if least greater than or equal + * to bound + */ + public double nextDouble(double least, double bound) { + if (least >= bound) + throw new IllegalArgumentException(); + return nextDouble() * (bound - least) + least; + } + + private static final long serialVersionUID = -5851777807851030925L; +} diff --git a/luni/src/main/java/java/util/concurrent/ThreadPoolExecutor.java b/luni/src/main/java/java/util/concurrent/ThreadPoolExecutor.java index 6622af8..331e225 100644 --- a/luni/src/main/java/java/util/concurrent/ThreadPoolExecutor.java +++ b/luni/src/main/java/java/util/concurrent/ThreadPoolExecutor.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; @@ -9,6 +9,10 @@ import java.util.concurrent.locks.*; import java.util.concurrent.atomic.*; import java.util.*; +// BEGIN android-note +// removed security manager docs +// END android-note + /** * An {@link ExecutorService} that executes each submitted task using * one of possibly several pooled threads, normally configured @@ -1311,8 +1315,6 @@ public class ThreadPoolExecutor extends AbstractExecutorService { * <p>This method does not wait for previously submitted tasks to * complete execution. Use {@link #awaitTermination awaitTermination} * to do that. - * - * @throws SecurityException {@inheritDoc} */ public void shutdown() { final ReentrantLock mainLock = this.mainLock; @@ -1342,8 +1344,6 @@ public class ThreadPoolExecutor extends AbstractExecutorService { * processing actively executing tasks. This implementation * cancels tasks via {@link Thread#interrupt}, so any task that * fails to respond to interrupts may never terminate. - * - * @throws SecurityException {@inheritDoc} */ public List<Runnable> shutdownNow() { List<Runnable> tasks; @@ -1512,6 +1512,18 @@ public class ThreadPoolExecutor extends AbstractExecutorService { } /** + * Same as prestartCoreThread except arranges that at least one + * thread is started even if corePoolSize is 0. + */ + void ensurePrestart() { + int wc = workerCountOf(ctl.get()); + if (wc < corePoolSize) + addWorker(null, true); + else if (wc == 0) + addWorker(null, false); + } + + /** * Starts all core threads, causing them to idly wait for work. This * overrides the default policy of starting core threads only when * new tasks are executed. diff --git a/luni/src/main/java/java/util/concurrent/TimeUnit.java b/luni/src/main/java/java/util/concurrent/TimeUnit.java index b2e3060..50f6ce0 100644 --- a/luni/src/main/java/java/util/concurrent/TimeUnit.java +++ b/luni/src/main/java/java/util/concurrent/TimeUnit.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; @@ -23,14 +23,14 @@ package java.util.concurrent; * the following code will timeout in 50 milliseconds if the {@link * java.util.concurrent.locks.Lock lock} is not available: * - * <pre> Lock lock = ...; - * if (lock.tryLock(50L, TimeUnit.MILLISECONDS)) ... - * </pre> + * <pre> {@code + * Lock lock = ...; + * if (lock.tryLock(50L, TimeUnit.MILLISECONDS)) ...}</pre> + * * while this code will timeout in 50 seconds: - * <pre> - * Lock lock = ...; - * if (lock.tryLock(50L, TimeUnit.SECONDS)) ... - * </pre> + * <pre> {@code + * Lock lock = ...; + * if (lock.tryLock(50L, TimeUnit.SECONDS)) ...}</pre> * * Note however, that there is no guarantee that a particular timeout * implementation will be able to notice the passage of time at the diff --git a/luni/src/main/java/java/util/concurrent/TimeoutException.java b/luni/src/main/java/java/util/concurrent/TimeoutException.java index 8b84f28..83934f0 100644 --- a/luni/src/main/java/java/util/concurrent/TimeoutException.java +++ b/luni/src/main/java/java/util/concurrent/TimeoutException.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent; diff --git a/luni/src/main/java/java/util/concurrent/TransferQueue.java b/luni/src/main/java/java/util/concurrent/TransferQueue.java new file mode 100644 index 0000000..9cd5773 --- /dev/null +++ b/luni/src/main/java/java/util/concurrent/TransferQueue.java @@ -0,0 +1,133 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +package java.util.concurrent; + +// BEGIN android-note +// removed link to collections framework docs +// END android-note + +/** + * A {@link BlockingQueue} in which producers may wait for consumers + * to receive elements. A {@code TransferQueue} may be useful for + * example in message passing applications in which producers + * sometimes (using method {@link #transfer}) await receipt of + * elements by consumers invoking {@code take} or {@code poll}, while + * at other times enqueue elements (via method {@code put}) without + * waiting for receipt. + * {@linkplain #tryTransfer(Object) Non-blocking} and + * {@linkplain #tryTransfer(Object,long,TimeUnit) time-out} versions of + * {@code tryTransfer} are also available. + * A {@code TransferQueue} may also be queried, via {@link + * #hasWaitingConsumer}, whether there are any threads waiting for + * items, which is a converse analogy to a {@code peek} operation. + * + * <p>Like other blocking queues, a {@code TransferQueue} may be + * capacity bounded. If so, an attempted transfer operation may + * initially block waiting for available space, and/or subsequently + * block waiting for reception by a consumer. Note that in a queue + * with zero capacity, such as {@link SynchronousQueue}, {@code put} + * and {@code transfer} are effectively synonymous. + * + * @since 1.7 + * @hide + * @author Doug Lea + * @param <E> the type of elements held in this collection + */ +public interface TransferQueue<E> extends BlockingQueue<E> { + /** + * Transfers the element to a waiting consumer immediately, if possible. + * + * <p>More precisely, transfers the specified element immediately + * if there exists a consumer already waiting to receive it (in + * {@link #take} or timed {@link #poll(long,TimeUnit) poll}), + * otherwise returning {@code false} without enqueuing the element. + * + * @param e the element to transfer + * @return {@code true} if the element was transferred, else + * {@code false} + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this queue + * @throws NullPointerException if the specified element is null + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this queue + */ + boolean tryTransfer(E e); + + /** + * Transfers the element to a consumer, waiting if necessary to do so. + * + * <p>More precisely, transfers the specified element immediately + * if there exists a consumer already waiting to receive it (in + * {@link #take} or timed {@link #poll(long,TimeUnit) poll}), + * else waits until the element is received by a consumer. + * + * @param e the element to transfer + * @throws InterruptedException if interrupted while waiting, + * in which case the element is not left enqueued + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this queue + * @throws NullPointerException if the specified element is null + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this queue + */ + void transfer(E e) throws InterruptedException; + + /** + * Transfers the element to a consumer if it is possible to do so + * before the timeout elapses. + * + * <p>More precisely, transfers the specified element immediately + * if there exists a consumer already waiting to receive it (in + * {@link #take} or timed {@link #poll(long,TimeUnit) poll}), + * else waits until the element is received by a consumer, + * returning {@code false} if the specified wait time elapses + * before the element can be transferred. + * + * @param e the element to transfer + * @param timeout how long to wait before giving up, in units of + * {@code unit} + * @param unit a {@code TimeUnit} determining how to interpret the + * {@code timeout} parameter + * @return {@code true} if successful, or {@code false} if + * the specified waiting time elapses before completion, + * in which case the element is not left enqueued + * @throws InterruptedException if interrupted while waiting, + * in which case the element is not left enqueued + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this queue + * @throws NullPointerException if the specified element is null + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this queue + */ + boolean tryTransfer(E e, long timeout, TimeUnit unit) + throws InterruptedException; + + /** + * Returns {@code true} if there is at least one consumer waiting + * to receive an element via {@link #take} or + * timed {@link #poll(long,TimeUnit) poll}. + * The return value represents a momentary state of affairs. + * + * @return {@code true} if there is at least one waiting consumer + */ + boolean hasWaitingConsumer(); + + /** + * Returns an estimate of the number of consumers waiting to + * receive elements via {@link #take} or timed + * {@link #poll(long,TimeUnit) poll}. The return value is an + * approximation of a momentary state of affairs, that may be + * inaccurate if consumers have completed or given up waiting. + * The value may be useful for monitoring and heuristics, but + * not for synchronization control. Implementations of this + * method are likely to be noticeably slower than those for + * {@link #hasWaitingConsumer}. + * + * @return the number of consumers waiting to receive elements + */ + int getWaitingConsumerCount(); +} diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java index c774d21..d531f25 100644 --- a/luni/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java +++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent.atomic; @@ -21,14 +21,14 @@ import sun.misc.Unsafe; public class AtomicBoolean implements java.io.Serializable { private static final long serialVersionUID = 4654671469794556979L; // setup to use Unsafe.compareAndSwapInt for updates - private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed + private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { - try { - valueOffset = unsafe.objectFieldOffset - (AtomicBoolean.class.getDeclaredField("value")); - } catch (Exception ex) { throw new Error(ex); } + try { + valueOffset = unsafe.objectFieldOffset + (AtomicBoolean.class.getDeclaredField("value")); + } catch (Exception ex) { throw new Error(ex); } } private volatile int value; diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicInteger.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicInteger.java index 16dd568..e0a0018 100644 --- a/luni/src/main/java/java/util/concurrent/atomic/AtomicInteger.java +++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicInteger.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent.atomic; @@ -24,14 +24,14 @@ public class AtomicInteger extends Number implements java.io.Serializable { private static final long serialVersionUID = 6214790243416807050L; // setup to use Unsafe.compareAndSwapInt for updates - private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed + private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { - try { - valueOffset = unsafe.objectFieldOffset - (AtomicInteger.class.getDeclaredField("value")); - } catch (Exception ex) { throw new Error(ex); } + try { + valueOffset = unsafe.objectFieldOffset + (AtomicInteger.class.getDeclaredField("value")); + } catch (Exception ex) { throw new Error(ex); } } private volatile int value; @@ -217,18 +217,33 @@ public class AtomicInteger extends Number implements java.io.Serializable { } + /** + * Returns the value of this {@code AtomicInteger} as an {@code int}. + */ public int intValue() { return get(); } + /** + * Returns the value of this {@code AtomicInteger} as a {@code long} + * after a widening primitive conversion. + */ public long longValue() { return (long)get(); } + /** + * Returns the value of this {@code AtomicInteger} as a {@code float} + * after a widening primitive conversion. + */ public float floatValue() { return (float)get(); } + /** + * Returns the value of this {@code AtomicInteger} as a {@code double} + * after a widening primitive conversion. + */ public double doubleValue() { return (double)get(); } diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java index 6dcdfd0..804a51e 100644 --- a/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java +++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java @@ -1,12 +1,11 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent.atomic; import sun.misc.Unsafe; -import java.util.*; /** * An {@code int} array in which elements may be updated atomically. @@ -19,7 +18,7 @@ import java.util.*; public class AtomicIntegerArray implements java.io.Serializable { private static final long serialVersionUID = 2862133569453604235L; - private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed + private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final int base = unsafe.arrayBaseOffset(int[].class); private static final int shift; private final int[] array; diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java index e8a0d57..c7ed158 100644 --- a/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java +++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent.atomic; @@ -236,24 +236,21 @@ public abstract class AtomicIntegerFieldUpdater<T> { * Standard hotspot implementation using intrinsics */ private static class AtomicIntegerFieldUpdaterImpl<T> extends AtomicIntegerFieldUpdater<T> { - private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed + private static final Unsafe unsafe = Unsafe.getUnsafe(); private final long offset; private final Class<T> tclass; - private final Class cclass; + private final Class<?> cclass; AtomicIntegerFieldUpdaterImpl(Class<T> tclass, String fieldName) { Field field = null; - Class caller = null; + Class<?> caller = null; int modifiers = 0; try { field = tclass.getDeclaredField(fieldName); - // BEGIN android-changed - caller = VMStack.getStackClass2(); - // END android-changed + caller = VMStack.getStackClass2(); // android-changed modifiers = field.getModifiers(); // BEGIN android-removed - // modifiers = field.getModifiers(); // sun.reflect.misc.ReflectUtil.ensureMemberAccess( // caller, tclass, null, modifiers); // sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass); @@ -262,7 +259,7 @@ public abstract class AtomicIntegerFieldUpdater<T> { throw new RuntimeException(ex); } - Class fieldt = field.getType(); + Class<?> fieldt = field.getType(); if (fieldt != int.class) throw new IllegalArgumentException("Must be integer type"); diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicLong.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicLong.java index 12d6cd1..5e799f7 100644 --- a/luni/src/main/java/java/util/concurrent/atomic/AtomicLong.java +++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicLong.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent.atomic; @@ -24,7 +24,7 @@ public class AtomicLong extends Number implements java.io.Serializable { private static final long serialVersionUID = 1927816293512124184L; // setup to use Unsafe.compareAndSwapLong for updates - private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed + private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; /** @@ -42,10 +42,10 @@ public class AtomicLong extends Number implements java.io.Serializable { private static native boolean VMSupportsCS8(); static { - try { - valueOffset = unsafe.objectFieldOffset - (AtomicLong.class.getDeclaredField("value")); - } catch (Exception ex) { throw new Error(ex); } + try { + valueOffset = unsafe.objectFieldOffset + (AtomicLong.class.getDeclaredField("value")); + } catch (Exception ex) { throw new Error(ex); } } private volatile long value; @@ -231,18 +231,33 @@ public class AtomicLong extends Number implements java.io.Serializable { } + /** + * Returns the value of this {@code AtomicLong} as an {@code int} + * after a narrowing primitive conversion. + */ public int intValue() { return (int)get(); } + /** + * Returns the value of this {@code AtomicLong} as a {@code long}. + */ public long longValue() { return get(); } + /** + * Returns the value of this {@code AtomicLong} as a {@code float} + * after a widening primitive conversion. + */ public float floatValue() { return (float)get(); } + /** + * Returns the value of this {@code AtomicLong} as a {@code double} + * after a widening primitive conversion. + */ public double doubleValue() { return (double)get(); } diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java index 9e2d25f..22edb3f 100644 --- a/luni/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java +++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java @@ -1,12 +1,11 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent.atomic; import sun.misc.Unsafe; -import java.util.*; /** * A {@code long} array in which elements may be updated atomically. @@ -18,7 +17,7 @@ import java.util.*; public class AtomicLongArray implements java.io.Serializable { private static final long serialVersionUID = -2308431214976778248L; - private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed + private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final int base = unsafe.arrayBaseOffset(long[].class); private static final int shift; private final long[] array; diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java index 21ef748..748ae69 100644 --- a/luni/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java +++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent.atomic; @@ -235,14 +235,14 @@ public abstract class AtomicLongFieldUpdater<T> { } private static class CASUpdater<T> extends AtomicLongFieldUpdater<T> { - private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed + private static final Unsafe unsafe = Unsafe.getUnsafe(); private final long offset; private final Class<T> tclass; - private final Class cclass; + private final Class<?> cclass; CASUpdater(Class<T> tclass, String fieldName) { Field field = null; - Class caller = null; + Class<?> caller = null; int modifiers = 0; try { field = tclass.getDeclaredField(fieldName); @@ -257,7 +257,7 @@ public abstract class AtomicLongFieldUpdater<T> { throw new RuntimeException(ex); } - Class fieldt = field.getType(); + Class<?> fieldt = field.getType(); if (fieldt != long.class) throw new IllegalArgumentException("Must be long type"); @@ -320,14 +320,14 @@ public abstract class AtomicLongFieldUpdater<T> { private static class LockedUpdater<T> extends AtomicLongFieldUpdater<T> { - private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed + private static final Unsafe unsafe = Unsafe.getUnsafe(); private final long offset; private final Class<T> tclass; - private final Class cclass; + private final Class<?> cclass; LockedUpdater(Class<T> tclass, String fieldName) { Field field = null; - Class caller = null; + Class<?> caller = null; int modifiers = 0; try { field = tclass.getDeclaredField(fieldName); @@ -342,7 +342,7 @@ public abstract class AtomicLongFieldUpdater<T> { throw new RuntimeException(ex); } - Class fieldt = field.getType(); + Class<?> fieldt = field.getType(); if (fieldt != long.class) throw new IllegalArgumentException("Must be long type"); diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java index 63f46d6..eaf700c 100644 --- a/luni/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java +++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java @@ -1,13 +1,11 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent.atomic; -import sun.misc.Unsafe; - /** * An {@code AtomicMarkableReference} maintains an object reference * along with a mark bit, that can be updated atomically. @@ -163,7 +161,7 @@ public class AtomicMarkableReference<V> { // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE = UnsafeAccess.THE_ONE; // android-changed + private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); private static final long pairOffset = objectFieldOffset(UNSAFE, "pair", AtomicMarkableReference.class); diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicReference.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicReference.java index f041bbd..b21e9b6 100644 --- a/luni/src/main/java/java/util/concurrent/atomic/AtomicReference.java +++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicReference.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent.atomic; @@ -18,14 +18,14 @@ import sun.misc.Unsafe; public class AtomicReference<V> implements java.io.Serializable { private static final long serialVersionUID = -1848883965231344442L; - private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed + private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { - try { - valueOffset = unsafe.objectFieldOffset - (AtomicReference.class.getDeclaredField("value")); - } catch (Exception ex) { throw new Error(ex); } + try { + valueOffset = unsafe.objectFieldOffset + (AtomicReference.class.getDeclaredField("value")); + } catch (Exception ex) { throw new Error(ex); } } private volatile V value; diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java index dbc5886..c47728d 100644 --- a/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java +++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java @@ -1,12 +1,14 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent.atomic; + +import java.util.Arrays; +import java.lang.reflect.Array; import sun.misc.Unsafe; -import java.util.*; /** * An array of object references in which elements may be updated @@ -20,13 +22,23 @@ import java.util.*; public class AtomicReferenceArray<E> implements java.io.Serializable { private static final long serialVersionUID = -6209656149925076980L; - private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed - private static final int base = unsafe.arrayBaseOffset(Object[].class); + private static final Unsafe unsafe; + private static final int base; private static final int shift; - private final Object[] array; + private static final long arrayFieldOffset; + private final Object[] array; // must have exact type Object[] static { - int scale = unsafe.arrayIndexScale(Object[].class); + int scale; + try { + unsafe = Unsafe.getUnsafe(); + arrayFieldOffset = unsafe.objectFieldOffset + (AtomicReferenceArray.class.getDeclaredField("array")); + base = unsafe.arrayBaseOffset(Object[].class); + scale = unsafe.arrayIndexScale(Object[].class); + } catch (Exception e) { + throw new Error(e); + } if ((scale & (scale - 1)) != 0) throw new Error("data type scale not a power of two"); shift = 31 - Integer.numberOfLeadingZeros(scale); @@ -45,7 +57,7 @@ public class AtomicReferenceArray<E> implements java.io.Serializable { /** * Creates a new AtomicReferenceArray of the given length, with all - * elements initially zero. + * elements initially null. * * @param length the length of the array */ @@ -62,7 +74,7 @@ public class AtomicReferenceArray<E> implements java.io.Serializable { */ public AtomicReferenceArray(E[] array) { // Visibility guaranteed by final field guarantees - this.array = array.clone(); + this.array = Arrays.copyOf(array, array.length, Object[].class); } /** @@ -121,7 +133,7 @@ public class AtomicReferenceArray<E> implements java.io.Serializable { public final E getAndSet(int i, E newValue) { long offset = checkedByteOffset(i); while (true) { - E current = (E) getRaw(offset); + E current = getRaw(offset); if (compareAndSetRaw(offset, current, newValue)) return current; } @@ -167,7 +179,7 @@ public class AtomicReferenceArray<E> implements java.io.Serializable { * @return the String representation of the current values of array */ public String toString() { - int iMax = array.length - 1; + int iMax = array.length - 1; if (iMax == -1) return "[]"; @@ -181,4 +193,20 @@ public class AtomicReferenceArray<E> implements java.io.Serializable { } } + /** + * Reconstitutes the instance from a stream (that is, deserializes it). + * @param s the stream + */ + private void readObject(java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException, + java.io.InvalidObjectException { + // Note: This must be changed if any additional fields are defined + Object a = s.readFields().get("array", null); + if (a == null || !a.getClass().isArray()) + throw new java.io.InvalidObjectException("Not array type"); + if (a.getClass() != Object[].class) + a = Arrays.copyOf((Object[])a, Array.getLength(a), Object[].class); + unsafe.putObjectVolatile(this, arrayFieldOffset, a); + } + } diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java index 8b3da0b..d23d766 100644 --- a/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java +++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java @@ -1,11 +1,11 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent.atomic; -import dalvik.system.VMStack; +import dalvik.system.VMStack; // android-added import sun.misc.Unsafe; import java.lang.reflect.*; @@ -155,7 +155,7 @@ public abstract class AtomicReferenceFieldUpdater<T, V> { private final long offset; private final Class<T> tclass; private final Class<V> vclass; - private final Class cclass; + private final Class<?> cclass; /* * Internal type checks within all update methods contain @@ -173,8 +173,8 @@ public abstract class AtomicReferenceFieldUpdater<T, V> { Class<V> vclass, String fieldName) { Field field = null; - Class fieldClass = null; - Class caller = null; + Class<?> fieldClass = null; + Class<?> caller = null; int modifiers = 0; try { field = tclass.getDeclaredField(fieldName); diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java index 2e826f2..a0cb492 100644 --- a/luni/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java +++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent.atomic; @@ -162,7 +162,7 @@ public class AtomicStampedReference<V> { // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE = UnsafeAccess.THE_ONE; // android-changed + private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); private static final long pairOffset = objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class); diff --git a/luni/src/main/java/java/util/concurrent/atomic/Fences.java b/luni/src/main/java/java/util/concurrent/atomic/Fences.java new file mode 100644 index 0000000..7ecf45a --- /dev/null +++ b/luni/src/main/java/java/util/concurrent/atomic/Fences.java @@ -0,0 +1,540 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +package java.util.concurrent.atomic; + +/** + * A set of methods providing fine-grained control over happens-before + * and synchronization order relations among reads and/or writes. The + * methods of this class are designed for use in uncommon situations + * where declaring variables {@code volatile} or {@code final}, using + * instances of atomic classes, using {@code synchronized} blocks or + * methods, or using other synchronization facilities are not possible + * or do not provide the desired control. + * + * <p><b>Memory Ordering.</b> There are three methods for controlling + * ordering relations among memory accesses (i.e., reads and + * writes). Method {@code orderWrites} is typically used to enforce + * order between two writes, and {@code orderAccesses} between a write + * and a read. Method {@code orderReads} is used to enforce order + * between two reads with respect to other {@code orderWrites} and/or + * {@code orderAccesses} invocations. The formally specified + * properties of these methods described below provide + * platform-independent guarantees that are honored by all levels of a + * platform (compilers, systems, processors). The use of these + * methods may result in the suppression of otherwise valid compiler + * transformations and optimizations that could visibly violate the + * specified orderings, and may or may not entail the use of + * processor-level "memory barrier" instructions. + * + * <p>Each ordering method accepts a {@code ref} argument, and + * controls ordering among accesses with respect to this reference. + * Invocations must be placed <em>between</em> accesses performed in + * expression evaluations and assignment statements to control the + * orderings of prior versus subsequent accesses appearing in program + * order. These methods also return their arguments to simplify + * correct usage in these contexts. + * + * <p>Usages of ordering methods almost always take one of the forms + * illustrated in the examples below. These idioms arrange some of + * the ordering properties associated with {@code volatile} and + * related language-based constructions, but without other + * compile-time and runtime benefits that make language-based + * constructions far better choices when they are applicable. Usages + * should be restricted to the control of strictly internal + * implementation matters inside a class or package, and must either + * avoid or document any consequent violations of ordering or safety + * properties expected by users of a class employing them. + * + * <p><b>Reachability.</b> Method {@code reachabilityFence} + * establishes an ordering for strong reachability (as defined in the + * {@link java.lang.ref} package specification) with respect to + * garbage collection. Method {@code reachabilityFence} differs from + * the others in that it controls relations that are otherwise only + * implicit in a program -- the reachability conditions triggering + * garbage collection. As illustrated in the sample usages below, + * this method is applicable only when reclamation may have visible + * effects, which is possible for objects with finalizers (see Section + * 12.6 of the Java Language Specification) that are implemented in + * ways that rely on ordering control for correctness. + * + * <p><b>Sample Usages</b> + * + * <p><b>Safe publication.</b> With care, method {@code orderWrites} + * may be used to obtain the memory safety effects of {@code final} + * for a field that cannot be declared as {@code final}, because its + * primary initialization cannot be performed in a constructor, in + * turn because it is used in a framework requiring that all classes + * have a no-argument constructor; as in: + * + * <pre> {@code + * class WidgetHolder { + * private Widget widget; + * public WidgetHolder() {} + * public static WidgetHolder newWidgetHolder(Params params) { + * WidgetHolder h = new WidgetHolder(); + * h.widget = new Widget(params); + * return Fences.orderWrites(h); + * } + * }}</pre> + * + * Here, the invocation of {@code orderWrites} ensures that the + * effects of the widget assignment are ordered before those of any + * (unknown) subsequent stores of {@code h} in other variables that + * make {@code h} available for use by other objects. Initialization + * sequences using {@code orderWrites} require more care than those + * involving {@code final} fields. When {@code final} is not used, + * compilers cannot help you to ensure that the field is set correctly + * across all usages. You must fully initialize objects + * <em>before</em> the {@code orderWrites} invocation that makes + * references to them safe to assign to accessible variables. Further, + * initialization sequences must not internally "leak" the reference + * by using it as an argument to a callback method or adding it to a + * static data structure. If less constrained usages were required, + * it may be possible to cope using more extensive sets of fences, or + * as a normally better choice, using synchronization (locking). + * Conversely, if it were possible to do so, the best option would be + * to rewrite class {@code WidgetHolder} to use {@code final}. + * + * <p>An alternative approach is to place similar mechanics in the + * (sole) method that makes such objects available for use by others. + * Here is a stripped-down example illustrating the essentials. In + * practice, among other changes, you would use access methods instead + * of a public field. + * + * <pre> {@code + * class AnotherWidgetHolder { + * public Widget widget; + * void publish(Widget w) { + * this.widget = Fences.orderWrites(w); + * } + * // ... + * }}</pre> + * + * In this case, the {@code orderWrites} invocation occurs before the + * store making the object available. Correctness again relies on + * ensuring that there are no leaks prior to invoking this method, and + * that it really is the <em>only</em> means of accessing the + * published object. This approach is not often applicable -- + * normally you would publish objects using a thread-safe collection + * that itself guarantees the expected ordering relations. However, it + * may come into play in the construction of such classes themselves. + * + * <p><b>Safely updating fields.</b> Outside of the initialization + * idioms illustrated above, Fence methods ordering writes must be + * paired with those ordering reads. To illustrate, suppose class + * {@code c} contains an accessible variable {@code data} that should + * have been declared as {@code volatile} but wasn't: + * + * <pre> {@code + * class C { + * Object data; // need volatile access but not volatile + * // ... + * } + * + * class App { + * Object getData(C c) { + * return Fences.orderReads(c).data; + * } + * + * void setData(C c) { + * Object newValue = ...; + * c.data = Fences.orderWrites(newValue); + * Fences.orderAccesses(c); + * } + * // ... + * }}</pre> + * + * Method {@code getData} provides an emulation of {@code volatile} + * reads of (non-long/double) fields by ensuring that the read of + * {@code c} obtained as an argument is ordered before subsequent + * reads using this reference, and then performs the read of its + * field. Method {@code setData} provides an emulation of volatile + * writes, ensuring that all other relevant writes have completed, + * then performing the assignment, and then ensuring that the write is + * ordered before any other access. These techniques may apply even + * when fields are not directly accessible, in which case calls to + * fence methods would surround calls to methods such as {@code + * c.getData()}. However, these techniques cannot be applied to + * {@code long} or {@code double} fields because reads and writes of + * fields of these types are not guaranteed to be + * atomic. Additionally, correctness may require that all accesses of + * such data use these kinds of wrapper methods, which you would need + * to manually ensure. + * + * <p>More generally, Fence methods can be used in this way to achieve + * the safety properties of {@code volatile}. However their use does + * not necessarily guarantee the full sequential consistency + * properties specified in the Java Language Specification chapter 17 + * for programs using {@code volatile}. In particular, emulation using + * Fence methods is not guaranteed to maintain the property that + * {@code volatile} operations performed by different threads are + * observed in the same order by all observer threads. + * + * <p><b>Acquire/Release management of threadsafe objects</b>. It may + * be possible to use weaker conventions for volatile-like variables + * when they are used to keep track of objects that fully manage their + * own thread-safety and synchronization. Here, an acquiring read + * operation remains the same as a volatile-read, but a releasing + * write differs by virtue of not itself ensuring an ordering of its + * write with subsequent reads, because the required effects are + * already ensured by the referenced objects. + * For example: + * + * <pre> {@code + * class Item { + * synchronized f(); // ALL methods are synchronized + * // ... + * } + * + * class ItemHolder { + * private Item item; + * Item acquireItem() { + * return Fences.orderReads(item); + * } + * + * void releaseItem(Item x) { + * item = Fences.orderWrites(x); + * } + * + * // ... + * }}</pre> + * + * Because this construction avoids use of {@code orderAccesses}, + * which is typically more costly than the other fence methods, it may + * result in better performance than using {@code volatile} or its + * emulation. However, as is the case with most applications of fence + * methods, correctness relies on the usage context -- here, the + * thread safety of {@code Item}, as well as the lack of need for full + * volatile semantics inside this class itself. However, the second + * concern means that it can be difficult to extend the {@code + * ItemHolder} class in this example to be more useful. + * + * <p><b>Avoiding premature finalization.</b> Finalization may occur + * whenever a Java Virtual Machine detects that no reference to an + * object will ever be stored in the heap: A garbage collector may + * reclaim an object even if the fields of that object are still in + * use, so long as the object has otherwise become unreachable. This + * may have surprising and undesirable effects in cases such as the + * following example in which the bookkeeping associated with a class + * is managed through array indices. Here, method {@code action} + * uses a {@code reachabilityFence} to ensure that the Resource + * object is not reclaimed before bookkeeping on an associated + * ExternalResource has been performed; in particular here, to ensure + * that the array slot holding the ExternalResource is not nulled out + * in method {@link Object#finalize}, which may otherwise run + * concurrently. + * + * <pre> {@code + * class Resource { + * private static ExternalResource[] externalResourceArray = ... + * + * int myIndex; + * Resource(...) { + * myIndex = ... + * externalResourceArray[myIndex] = ...; + * ... + * } + * protected void finalize() { + * externalResourceArray[myIndex] = null; + * ... + * } + * public void action() { + * try { + * // ... + * int i = myIndex; + * Resource.update(externalResourceArray[i]); + * } finally { + * Fences.reachabilityFence(this); + * } + * } + * private static void update(ExternalResource ext) { + * ext.status = ...; + * } + * }}</pre> + * + * Here, the call to {@code reachabilityFence} is nonintuitively + * placed <em>after</em> the call to {@code update}, to ensure that + * the array slot is not nulled out by {@link Object#finalize} before + * the update, even if the call to {@code action} was the last use of + * this object. This might be the case if for example a usage in a + * user program had the form {@code new Resource().action();} which + * retains no other reference to this Resource. While probably + * overkill here, {@code reachabilityFence} is placed in a {@code + * finally} block to ensure that it is invoked across all paths in the + * method. In a method with more complex control paths, you might + * need further precautions to ensure that {@code reachabilityFence} + * is encountered along all of them. + * + * <p>It is sometimes possible to better encapsulate use of + * {@code reachabilityFence}. Continuing the above example, if it + * were OK for the call to method update to proceed even if the + * finalizer had already executed (nulling out slot), then you could + * localize use of {@code reachabilityFence}: + * + * <pre> {@code + * public void action2() { + * // ... + * Resource.update(getExternalResource()); + * } + * private ExternalResource getExternalResource() { + * ExternalResource ext = externalResourceArray[myIndex]; + * Fences.reachabilityFence(this); + * return ext; + * }}</pre> + * + * <p>Method {@code reachabilityFence} is not required in + * constructions that themselves ensure reachability. For example, + * because objects that are locked cannot in general be reclaimed, it + * would suffice if all accesses of the object, in all methods of + * class Resource (including {@code finalize}) were enclosed in {@code + * synchronized (this)} blocks. (Further, such blocks must not include + * infinite loops, or themselves be unreachable, which fall into the + * corner case exceptions to the "in general" disclaimer.) However, + * method {@code reachabilityFence} remains a better option in cases + * where this approach is not as efficient, desirable, or possible; + * for example because it would encounter deadlock. + * + * <p><b>Formal Properties.</b> + * + * <p>Using the terminology of The Java Language Specification chapter + * 17, the rules governing the semantics of the methods of this class + * are as follows: + * + * <p> The following is still under construction. + * + * <dl> + * + * <dt><b>[Definitions]</b> + * <dd> + * <ul> + * + * <li>Define <em>sequenced(a, b)</em> to be true if <em>a</em> + * occurs before <em>b</em> in <em>program order</em>. + * + * <li>Define <em>accesses(a, p)</em> to be true if + * <em>a</em> is a read or write of a field (or if an array, an + * element) of the object referenced by <em>p</em>. + * + * <li>Define <em>deeplyAccesses(a, p)</em> to be true if either + * <em>accesses(a, p)</em> or <em>deeplyAccesses(a, q)</em> where + * <em>q</em> is the value seen by some read <em>r</em> + * such that <em>accesses(r, p)</em>. + * + * </ul> + * <p> + * <dt><b>[Matching]</b> + * <dd> Given: + * + * <ul> + * + * <li><em>p</em>, a reference to an object + * + * <li><em>wf</em>, an invocation of {@code orderWrites(p)} or + * {@code orderAccesses(p)} + * + * <li><em>w</em>, a write of value <em>p</em> + * + * <li> <em>rf</em>, an invocation of {@code orderReads(p)} or + * {@code orderAccesses(p)} + * + * <li> <em>r</em>, a read returning value <em>p</em> + * + * </ul> + * If: + * <ul> + * <li>sequenced(wf, w) + * <li>read <em>r</em> sees write <em>w</em> + * <li>sequenced(r, rf) + * </ul> + * Then: + * <ul> + * + * <li> <em>wf happens-before rf</em> + * + * <li> <em>wf</em> precedes <em>rf</em> in the + * <em>synchronization order</em> + * + * <li> If (<em>r1</em>, <em>w1</em>) and (<em>r2</em>, + * <em>w2</em>) are two pairs of reads and writes, both + * respectively satisfying the above conditions for <em>p</em>, + * and sequenced(r1, r2) then it is not the case that <em>w2 + * happens-before w1</em>. + * + * </ul> + * <p> + * <dt><b>[Initial Reads]</b> + * <dd> Given: + * + * <ul> + * + * <li><em>p</em>, a reference to an object + * + * <li> <em>a</em>, an access where deeplyAccesses(a, p) + * + * <li><em>wf</em>, an invocation of {@code orderWrites(p)} or + * {@code orderAccesses(p)} + * + * <li><em>w</em>, a write of value <em>p</em> + * + * <li> <em>r</em>, a read returning value <em>p</em> + * + * <li> <em>b</em>, an access where accesses(b, p) + * + * </ul> + * If: + * <ul> + * <li>sequenced(a, wf); + * <li>sequenced(wf, w) + * <li>read <em>r</em> sees write <em>w</em>, and + * <em>r</em> is the first read by some thread + * <em>t</em> that sees value <em>p</em> + * <li>sequenced(r, b) + * </ul> + * Then: + * <ul> + * <li> the effects of <em>b</em> are constrained + * by the relation <em>a happens-before b</em>. + * </ul> + * <p> + * <dt><b>[orderAccesses]</b> + * <dd> Given: + * + * <ul> + * <li><em>p</em>, a reference to an object + * <li><em>f</em>, an invocation of {@code orderAccesses(p)} + * </ul> + * If: + * <ul> + * <li>sequenced(f, w) + * </ul> + * + * Then: + * + * <ul> + * + * <li> <em>f</em> is an element of the <em>synchronization order</em>. + * + * </ul> + * <p> + * <dt><b>[Reachability]</b> + * <dd> Given: + * + * <ul> + * + * <li><em>p</em>, a reference to an object + * + * <li><em>f</em>, an invocation of {@code reachabilityFence(p)} + * + * <li><em>a</em>, an access where accesses(a, p) + * + * <li><em>b</em>, an action (by a garbage collector) taking + * the form of an invocation of {@code + * p.finalize()} or of enqueing any {@link + * java.lang.ref.Reference} constructed with argument <em>p</em> + * + * </ul> + * + * If: + * <ul> + * <li>sequenced(a, f) + * </ul> + * + * Then: + * + * <ul> + * + * <li> <em>a happens-before b</em>. + * + * </ul> + * + * </dl> + * + * @since 1.7 + * @hide + * @author Doug Lea + */ +public class Fences { + private Fences() {} // Non-instantiable + + /* + * The methods of this class are intended to be intrinisified by a + * JVM. However, we provide correct but inefficient Java-level + * code that simply reads and writes a static volatile + * variable. Without JVM support, the consistency effects are + * stronger than necessary, and the memory contention effects can + * be a serious performance issue. + */ + private static volatile int theVolatile; + + /** + * Informally: Ensures that a read of the given reference prior to + * the invocation of this method occurs before a subsequent use of + * the given reference with the effect of reading or writing a + * field (or if an array, element) of the referenced object. The + * use of this method is sensible only when paired with other + * invocations of {@link #orderWrites} and/or {@link + * #orderAccesses} for the given reference. For details, see the + * class documentation for this class. + * + * @param ref the reference. If null, this method has no effect. + * @return the given ref, to simplify usage + */ + public static <T> T orderReads(T ref) { + int ignore = theVolatile; + return ref; + } + + /** + * Informally: Ensures that a use of the given reference with the + * effect of reading or writing a field (or if an array, element) + * of the referenced object, prior to the invocation of this + * method occur before a subsequent write of the reference. For + * details, see the class documentation for this class. + * + * @param ref the reference. If null, this method has no effect. + * @return the given ref, to simplify usage + */ + public static <T> T orderWrites(T ref) { + theVolatile = 0; + return ref; + } + + /** + * Informally: Ensures that accesses (reads or writes) using the + * given reference prior to the invocation of this method occur + * before subsequent accesses. For details, see the class + * documentation for this class. + * + * @param ref the reference. If null, this method has no effect. + * @return the given ref, to simplify usage + */ + public static <T> T orderAccesses(T ref) { + theVolatile = 0; + return ref; + } + + /** + * Ensures that the object referenced by the given reference + * remains <em>strongly reachable</em> (as defined in the {@link + * java.lang.ref} package documentation), regardless of any prior + * actions of the program that might otherwise cause the object to + * become unreachable; thus, the referenced object is not + * reclaimable by garbage collection at least until after the + * invocation of this method. Invocation of this method does not + * itself initiate garbage collection or finalization. + * + * <p>See the class-level documentation for further explanation + * and usage examples. + * + * @param ref the reference. If null, this method has no effect. + */ + public static void reachabilityFence(Object ref) { + if (ref != null) { + synchronized (ref) {} + } + } +} diff --git a/luni/src/main/java/java/util/concurrent/atomic/package-info.java b/luni/src/main/java/java/util/concurrent/atomic/package-info.java index 4a4375d..efbb413 100644 --- a/luni/src/main/java/java/util/concurrent/atomic/package-info.java +++ b/luni/src/main/java/java/util/concurrent/atomic/package-info.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ /** @@ -11,9 +11,7 @@ * array elements to those that also provide an atomic conditional update * operation of the form: * - * <pre> - * boolean compareAndSet(expectedValue, updateValue); - * </pre> + * <pre> {@code boolean compareAndSet(expectedValue, updateValue);}</pre> * * <p>This method (which varies in argument types across different * classes) atomically sets a variable to the {@code updateValue} if it @@ -40,15 +38,30 @@ * {@code AtomicInteger} provide atomic increment methods. One * application is to generate sequence numbers, as in: * - * <pre> + * <pre> {@code * class Sequencer { * private final AtomicLong sequenceNumber * = new AtomicLong(0); * public long next() { * return sequenceNumber.getAndIncrement(); * } - * } - * </pre> + * }}</pre> + * + * <p>It is straightforward to define new utility functions that, like + * {@code getAndIncrement}, apply a function to a value atomically. + * For example, given some transformation + * <pre> {@code long transform(long input)}</pre> + * + * write your utility method as follows: + * <pre> {@code + * boolean getAndTransform(AtomicLong var) { + * while (true) { + * long current = var.get(); + * long next = transform(current); + * if (var.compareAndSet(current, next)) + * return current; + * } + * }}</pre> * * <p>The memory effects for accesses and updates of atomics generally * follow the rules for volatiles, as stated in @@ -161,9 +174,9 @@ * {@code byte} values, and cast appropriately. * * You can also hold floats using - * {@link java.lang.Float#floatToIntBits} and + * {@link java.lang.Float#floatToRawIntBits} and * {@link java.lang.Float#intBitsToFloat} conversions, and doubles using - * {@link java.lang.Double#doubleToLongBits} and + * {@link java.lang.Double#doubleToRawLongBits} and * {@link java.lang.Double#longBitsToDouble} conversions. * * @since 1.5 diff --git a/luni/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java b/luni/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java index f3780e5..4bec0cf 100644 --- a/luni/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java +++ b/luni/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent.locks; diff --git a/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java b/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java index 5c8111c..7b36460 100644 --- a/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java +++ b/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java @@ -1,13 +1,12 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent.locks; import java.util.*; import java.util.concurrent.*; -import java.util.concurrent.atomic.*; import sun.misc.Unsafe; /** @@ -569,7 +568,7 @@ public abstract class AbstractQueuedLongSynchronizer /** * Convenience method to interrupt current thread. */ - private static void selfInterrupt() { + static void selfInterrupt() { Thread.currentThread().interrupt(); } @@ -1231,7 +1230,7 @@ public abstract class AbstractQueuedLongSynchronizer * due to the queue being empty. * * <p>This method is designed to be used by a fair synchronizer to - * avoid <a href="AbstractQueuedSynchronizer#barging">barging</a>. + * avoid <a href="AbstractQueuedSynchronizer.html#barging">barging</a>. * Such a synchronizer's {@link #tryAcquire} method should return * {@code false}, and its {@link #tryAcquireShared} method should * return a negative value, if this method returns {@code true} diff --git a/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java b/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java index 065f130..42029f0 100644 --- a/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java +++ b/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java @@ -1,13 +1,12 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent.locks; import java.util.*; import java.util.concurrent.*; -import java.util.concurrent.atomic.*; import sun.misc.Unsafe; // BEGIN android-note @@ -173,7 +172,7 @@ import sun.misc.Unsafe; * It also supports conditions and exposes * one of the instrumentation methods: * - * <pre> + * <pre> {@code * class Mutex implements Lock, java.io.Serializable { * * // Our internal helper class @@ -229,15 +228,14 @@ import sun.misc.Unsafe; * throws InterruptedException { * return sync.tryAcquireNanos(1, unit.toNanos(timeout)); * } - * } - * </pre> + * }}</pre> * * <p>Here is a latch class that is like a {@link CountDownLatch} * except that it only requires a single <tt>signal</tt> to * fire. Because a latch is non-exclusive, it uses the <tt>shared</tt> * acquire and release methods. * - * <pre> + * <pre> {@code * class BooleanLatch { * * private static class Sync extends AbstractQueuedSynchronizer { @@ -259,8 +257,7 @@ import sun.misc.Unsafe; * public void await() throws InterruptedException { * sync.acquireSharedInterruptibly(1); * } - * } - * </pre> + * }}</pre> * * @since 1.5 * @author Doug Lea @@ -800,7 +797,7 @@ public abstract class AbstractQueuedSynchronizer /** * Convenience method to interrupt current thread. */ - private static void selfInterrupt() { + static void selfInterrupt() { Thread.currentThread().interrupt(); } @@ -2251,9 +2248,7 @@ public abstract class AbstractQueuedSynchronizer * are at it, we do the same for other CASable fields (which could * otherwise be done with atomic field updaters). */ - // BEGIN android-changed - private static final Unsafe unsafe = UnsafeAccess.THE_ONE; - // END android-changed + private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long stateOffset; private static final long headOffset; private static final long tailOffset; diff --git a/luni/src/main/java/java/util/concurrent/locks/Condition.java b/luni/src/main/java/java/util/concurrent/locks/Condition.java index 8504e1f..7050df9 100644 --- a/luni/src/main/java/java/util/concurrent/locks/Condition.java +++ b/luni/src/main/java/java/util/concurrent/locks/Condition.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent.locks; @@ -331,10 +331,9 @@ public interface Condition { /** * Causes the current thread to wait until it is signalled or interrupted, * or the specified waiting time elapses. This method is behaviorally - * equivalent to:<br> - * <pre> - * awaitNanos(unit.toNanos(time)) > 0 - * </pre> + * equivalent to: + * <pre> {@code awaitNanos(unit.toNanos(time)) > 0}</pre> + * * @param time the maximum time to wait * @param unit the time unit of the {@code time} argument * @return {@code false} if the waiting time detectably elapsed diff --git a/luni/src/main/java/java/util/concurrent/locks/Lock.java b/luni/src/main/java/java/util/concurrent/locks/Lock.java index 4b9abd6..d5c6294 100644 --- a/luni/src/main/java/java/util/concurrent/locks/Lock.java +++ b/luni/src/main/java/java/util/concurrent/locks/Lock.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent.locks; @@ -48,14 +48,14 @@ import java.util.concurrent.TimeUnit; * methods and statements. In most cases, the following idiom * should be used: * - * <pre><tt> Lock l = ...; - * l.lock(); - * try { - * // access the resource protected by this lock - * } finally { - * l.unlock(); - * } - * </tt></pre> + * <pre> {@code + * Lock l = ...; + * l.lock(); + * try { + * // access the resource protected by this lock + * } finally { + * l.unlock(); + * }}</pre> * * When locking and unlocking occur in different scopes, care must be * taken to ensure that all code that is executed while the lock is @@ -210,18 +210,18 @@ public interface Lock { * immediately with the value {@code false}. * * <p>A typical usage idiom for this method would be: - * <pre> - * Lock lock = ...; - * if (lock.tryLock()) { - * try { - * // manipulate protected state - * } finally { - * lock.unlock(); - * } - * } else { - * // perform alternative actions - * } - * </pre> + * <pre> {@code + * Lock lock = ...; + * if (lock.tryLock()) { + * try { + * // manipulate protected state + * } finally { + * lock.unlock(); + * } + * } else { + * // perform alternative actions + * }}</pre> + * * This usage ensures that the lock is unlocked if it was acquired, and * doesn't try to unlock if the lock was not acquired. * diff --git a/luni/src/main/java/java/util/concurrent/locks/LockSupport.java b/luni/src/main/java/java/util/concurrent/locks/LockSupport.java index 0c0f07d..422e428 100644 --- a/luni/src/main/java/java/util/concurrent/locks/LockSupport.java +++ b/luni/src/main/java/java/util/concurrent/locks/LockSupport.java @@ -1,11 +1,10 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent.locks; -import java.util.concurrent.*; import sun.misc.Unsafe; @@ -49,7 +48,10 @@ import sun.misc.Unsafe; * higher-level synchronization utilities, and are not in themselves * useful for most concurrency control applications. The {@code park} * method is designed for use only in constructions of the form: - * <pre>while (!canProceed()) { ... LockSupport.park(this); }</pre> + * + * <pre> {@code + * while (!canProceed()) { ... LockSupport.park(this); }}</pre> + * * where neither {@code canProceed} nor any other actions prior to the * call to {@code park} entail locking or blocking. Because only one * permit is associated with each thread, any intermediary uses of @@ -57,7 +59,7 @@ import sun.misc.Unsafe; * * <p><b>Sample Usage.</b> Here is a sketch of a first-in-first-out * non-reentrant lock class: - * <pre>{@code + * <pre> {@code * class FIFOMutex { * private final AtomicBoolean locked = new AtomicBoolean(false); * private final Queue<Thread> waiters @@ -92,7 +94,7 @@ public class LockSupport { private LockSupport() {} // Cannot be instantiated. // Hotspot implementation via intrinsics API - private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // android-changed + private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long parkBlockerOffset; static { @@ -246,10 +248,14 @@ public class LockSupport { * snapshot -- the thread may have since unblocked or blocked on a * different blocker object. * + * @param t the thread * @return the blocker + * @throws NullPointerException if argument is null * @since 1.6 */ public static Object getBlocker(Thread t) { + if (t == null) + throw new NullPointerException(); return unsafe.getObjectVolatile(t, parkBlockerOffset); } diff --git a/luni/src/main/java/java/util/concurrent/locks/ReadWriteLock.java b/luni/src/main/java/java/util/concurrent/locks/ReadWriteLock.java index 484f68d..bb7b388 100644 --- a/luni/src/main/java/java/util/concurrent/locks/ReadWriteLock.java +++ b/luni/src/main/java/java/util/concurrent/locks/ReadWriteLock.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent.locks; diff --git a/luni/src/main/java/java/util/concurrent/locks/ReentrantLock.java b/luni/src/main/java/java/util/concurrent/locks/ReentrantLock.java index cf787ca..07baf41 100644 --- a/luni/src/main/java/java/util/concurrent/locks/ReentrantLock.java +++ b/luni/src/main/java/java/util/concurrent/locks/ReentrantLock.java @@ -1,13 +1,12 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent.locks; import java.util.*; import java.util.concurrent.*; -import java.util.concurrent.atomic.*; /** * A reentrant mutual exclusion {@link Lock} with the same basic @@ -44,7 +43,7 @@ import java.util.concurrent.atomic.*; * follow a call to {@code lock} with a {@code try} block, most * typically in a before/after construction such as: * - * <pre> + * <pre> {@code * class X { * private final ReentrantLock lock = new ReentrantLock(); * // ... @@ -57,8 +56,7 @@ import java.util.concurrent.atomic.*; * lock.unlock() * } * } - * } - * </pre> + * }}</pre> * * <p>In addition to implementing the {@link Lock} interface, this * class defines methods {@code isLocked} and @@ -354,8 +352,11 @@ public class ReentrantLock implements Lock, java.io.Serializable { * method. If you want a timed {@code tryLock} that does permit barging on * a fair lock then combine the timed and un-timed forms together: * - * <pre>if (lock.tryLock() || lock.tryLock(timeout, unit) ) { ... } - * </pre> + * <pre> {@code + * if (lock.tryLock() || + * lock.tryLock(timeout, unit)) { + * ... + * }}</pre> * * <p>If the current thread * already holds this lock then the hold count is incremented by one and @@ -485,7 +486,7 @@ public class ReentrantLock implements Lock, java.io.Serializable { * not be entered with the lock already held then we can assert that * fact: * - * <pre> + * <pre> {@code * class X { * ReentrantLock lock = new ReentrantLock(); * // ... @@ -498,8 +499,7 @@ public class ReentrantLock implements Lock, java.io.Serializable { * lock.unlock(); * } * } - * } - * </pre> + * }}</pre> * * @return the number of holds on this lock by the current thread, * or zero if this lock is not held by the current thread @@ -516,7 +516,7 @@ public class ReentrantLock implements Lock, java.io.Serializable { * testing. For example, a method that should only be called while * a lock is held can assert that this is the case: * - * <pre> + * <pre> {@code * class X { * ReentrantLock lock = new ReentrantLock(); * // ... @@ -525,13 +525,12 @@ public class ReentrantLock implements Lock, java.io.Serializable { * assert lock.isHeldByCurrentThread(); * // ... method body * } - * } - * </pre> + * }}</pre> * * <p>It can also be used to ensure that a reentrant lock is used * in a non-reentrant manner, for example: * - * <pre> + * <pre> {@code * class X { * ReentrantLock lock = new ReentrantLock(); * // ... @@ -545,8 +544,7 @@ public class ReentrantLock implements Lock, java.io.Serializable { * lock.unlock(); * } * } - * } - * </pre> + * }}</pre> * * @return {@code true} if current thread holds this lock and * {@code false} otherwise diff --git a/luni/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java b/luni/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java index b1a1a60..244a4a7 100644 --- a/luni/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java +++ b/luni/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java @@ -1,12 +1,11 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ package java.util.concurrent.locks; import java.util.concurrent.*; -import java.util.concurrent.atomic.*; import java.util.*; /** @@ -33,7 +32,7 @@ import java.util.*; * <dt><b><i>Fair mode</i></b> * <dd> When constructed as fair, threads contend for entry using an * approximately arrival-order policy. When the currently held lock - * is released either the longest-waiting single writer thread will + * is released, either the longest-waiting single writer thread will * be assigned the write lock, or if there is a group of reader threads * waiting longer than all waiting writer threads, that group will be * assigned the read lock. @@ -51,8 +50,8 @@ import java.util.*; * will block unless both the read lock and write lock are free (which * implies there are no waiting threads). (Note that the non-blocking * {@link ReadLock#tryLock()} and {@link WriteLock#tryLock()} methods - * do not honor this fair setting and will acquire the lock if it is - * possible, regardless of waiting threads.) + * do not honor this fair setting and will immediately acquire the lock + * if it is possible, regardless of waiting threads.) * <p> * </dl> * @@ -114,21 +113,21 @@ import java.util.*; * void processCachedData() { * rwl.readLock().lock(); * if (!cacheValid) { - * // Must release read lock before acquiring write lock - * rwl.readLock().unlock(); - * rwl.writeLock().lock(); - * try { - * // Recheck state because another thread might have - * // acquired write lock and changed state before we did. - * if (!cacheValid) { - * data = ... - * cacheValid = true; - * } - * // Downgrade by acquiring read lock before releasing write lock - * rwl.readLock().lock(); - * } finally { - * rwl.writeLock().unlock(); // Unlock write, still hold read - * } + * // Must release read lock before acquiring write lock + * rwl.readLock().unlock(); + * rwl.writeLock().lock(); + * try { + * // Recheck state because another thread might have + * // acquired write lock and changed state before we did. + * if (!cacheValid) { + * data = ... + * cacheValid = true; + * } + * // Downgrade by acquiring read lock before releasing write lock + * rwl.readLock().lock(); + * } finally { + * rwl.writeLock().unlock(); // Unlock write, still hold read + * } * } * * try { @@ -147,33 +146,33 @@ import java.util.*; * is a class using a TreeMap that is expected to be large and * concurrently accessed. * - * <pre>{@code + * <pre> {@code * class RWDictionary { - * private final Map<String, Data> m = new TreeMap<String, Data>(); - * private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); - * private final Lock r = rwl.readLock(); - * private final Lock w = rwl.writeLock(); + * private final Map<String, Data> m = new TreeMap<String, Data>(); + * private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); + * private final Lock r = rwl.readLock(); + * private final Lock w = rwl.writeLock(); * - * public Data get(String key) { - * r.lock(); - * try { return m.get(key); } - * finally { r.unlock(); } - * } - * public String[] allKeys() { - * r.lock(); - * try { return m.keySet().toArray(); } - * finally { r.unlock(); } - * } - * public Data put(String key, Data value) { - * w.lock(); - * try { return m.put(key, value); } - * finally { w.unlock(); } - * } - * public void clear() { - * w.lock(); - * try { m.clear(); } - * finally { w.unlock(); } - * } + * public Data get(String key) { + * r.lock(); + * try { return m.get(key); } + * finally { r.unlock(); } + * } + * public String[] allKeys() { + * r.lock(); + * try { return m.keySet().toArray(); } + * finally { r.unlock(); } + * } + * public Data put(String key, Data value) { + * w.lock(); + * try { return m.put(key, value); } + * finally { w.unlock(); } + * } + * public void clear() { + * w.lock(); + * try { m.clear(); } + * finally { w.unlock(); } + * } * }}</pre> * * <h3>Implementation Notes</h3> @@ -625,7 +624,9 @@ public class ReentrantReadWriteLock } /** - * Reconstitute this lock instance from a stream + * Reconstitutes this lock instance from a stream (that is, + * deserializes it). + * * @param s the stream */ private void readObject(java.io.ObjectInputStream s) @@ -790,8 +791,11 @@ public class ReentrantReadWriteLock * permit barging on a fair lock then combine the timed and * un-timed forms together: * - * <pre>if (lock.tryLock() || lock.tryLock(timeout, unit) ) { ... } - * </pre> + * <pre> {@code + * if (lock.tryLock() || + * lock.tryLock(timeout, unit)) { + * ... + * }}</pre> * * <p>If the write lock is held by another thread then the * current thread becomes disabled for thread scheduling @@ -1020,8 +1024,11 @@ public class ReentrantReadWriteLock * that does permit barging on a fair lock then combine the * timed and un-timed forms together: * - * <pre>if (lock.tryLock() || lock.tryLock(timeout, unit) ) { ... } - * </pre> + * <pre> {@code + * if (lock.tryLock() || + * lock.tryLock(timeout, unit)) { + * ... + * }}</pre> * * <p>If the current thread already holds this lock then the * hold count is incremented by one and the method returns diff --git a/luni/src/main/java/java/util/concurrent/locks/package-info.java b/luni/src/main/java/java/util/concurrent/locks/package-info.java index 860acdd..433f869 100644 --- a/luni/src/main/java/java/util/concurrent/locks/package-info.java +++ b/luni/src/main/java/java/util/concurrent/locks/package-info.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ /** diff --git a/luni/src/main/java/java/util/concurrent/package-info.java b/luni/src/main/java/java/util/concurrent/package-info.java index 8509a41..155d1b8 100644 --- a/luni/src/main/java/java/util/concurrent/package-info.java +++ b/luni/src/main/java/java/util/concurrent/package-info.java @@ -1,11 +1,11 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain + * http://creativecommons.org/publicdomain/zero/1.0/ */ // BEGIN android-note -// Dropped references to unreleased APIs (ForkJoinPool, Phaser, etc.) +// omit links to ForkJoinPool, ForkJoinTask, LinkedTransferQueue, PHaser, TransferQueue // END android-note /** @@ -111,7 +111,7 @@ * * <h2>Synchronizers</h2> * - * Five classes aid common special-purpose synchronization idioms. + * Four classes aid common special-purpose synchronization idioms. * <ul> * * <li>{@link java.util.concurrent.Semaphore} is a classic concurrency tool. @@ -146,7 +146,7 @@ * A {@code CopyOnWriteArrayList} is preferable to a synchronized * {@code ArrayList} when the expected number of reads and traversals * greatly outnumber the number of updates to a list. - + * * <p>The "Concurrent" prefix used with some classes in this package * is a shorthand indicating several differences from similar * "synchronized" classes. For example {@code java.util.Hashtable} and @@ -243,7 +243,8 @@ * in each thread <i>happen-before</i> those subsequent to the * corresponding {@code exchange()} in another thread. * - * <li>Actions prior to calling {@code CyclicBarrier.await} + * <li>Actions prior to calling {@code CyclicBarrier.await} and + * {@code Phaser.awaitAdvance} (as well as its variants) * <i>happen-before</i> actions performed by the barrier action, and * actions performed by the barrier action <i>happen-before</i> actions * subsequent to a successful return from the corresponding {@code await} diff --git a/luni/src/main/java/java/util/jar/JarOutputStream.java b/luni/src/main/java/java/util/jar/JarOutputStream.java index 78e3aa5..fc0645e 100644 --- a/luni/src/main/java/java/util/jar/JarOutputStream.java +++ b/luni/src/main/java/java/util/jar/JarOutputStream.java @@ -37,20 +37,20 @@ public class JarOutputStream extends ZipOutputStream { * * @param os * the {@code OutputStream} to write to - * @param mf + * @param manifest * the {@code Manifest} to output for this JAR file. * @throws IOException * if an error occurs creating the {@code JarOutputStream}. */ - public JarOutputStream(OutputStream os, Manifest mf) throws IOException { + public JarOutputStream(OutputStream os, Manifest manifest) throws IOException { super(os); - if (mf == null) { - throw new NullPointerException(); + if (manifest == null) { + throw new NullPointerException("manifest == null"); } - manifest = mf; + this.manifest = manifest; ZipEntry ze = new ZipEntry(JarFile.MANIFEST_NAME); putNextEntry(ze); - manifest.write(this); + this.manifest.write(this); closeEntry(); } diff --git a/luni/src/main/java/java/util/logging/FileHandler.java b/luni/src/main/java/java/util/logging/FileHandler.java index 980955a..6ffef87 100644 --- a/luni/src/main/java/java/util/logging/FileHandler.java +++ b/luni/src/main/java/java/util/logging/FileHandler.java @@ -214,8 +214,10 @@ public class FileHandler extends StreamHandler { String className = this.getClass().getName(); pattern = (p == null) ? getStringProperty(className + ".pattern", DEFAULT_PATTERN) : p; - if (pattern == null || pattern.isEmpty()) { - throw new NullPointerException("Pattern cannot be empty or null"); + if (pattern == null) { + throw new NullPointerException("pattern == null"); + } else if (pattern.isEmpty()) { + throw new NullPointerException("pattern.isEmpty()"); } append = (a == null) ? getBooleanProperty(className + ".append", DEFAULT_APPEND) : a.booleanValue(); diff --git a/luni/src/main/java/java/util/logging/Handler.java b/luni/src/main/java/java/util/logging/Handler.java index 5a6c14e..13dbdd5 100644 --- a/luni/src/main/java/java/util/logging/Handler.java +++ b/luni/src/main/java/java/util/logging/Handler.java @@ -227,7 +227,7 @@ public abstract class Handler { */ public boolean isLoggable(LogRecord record) { if (record == null) { - throw new NullPointerException(); + throw new NullPointerException("record == null"); } if (this.level.intValue() == Level.OFF.intValue()) { return false; @@ -294,17 +294,17 @@ public abstract class Handler { /** * Sets the error manager for this handler. * - * @param em + * @param newErrorManager * the error manager to set. * @throws NullPointerException * if {@code em} is {@code null}. */ - public void setErrorManager(ErrorManager em) { + public void setErrorManager(ErrorManager newErrorManager) { LogManager.getLogManager().checkAccess(); - if (em == null) { - throw new NullPointerException(); + if (newErrorManager == null) { + throw new NullPointerException("newErrorManager == null"); } - this.errorMan = em; + this.errorMan = newErrorManager; } /** @@ -327,7 +327,7 @@ public abstract class Handler { */ void internalSetFormatter(Formatter newFormatter) { if (newFormatter == null) { - throw new NullPointerException(); + throw new NullPointerException("newFormatter == null"); } this.formatter = newFormatter; } @@ -356,7 +356,7 @@ public abstract class Handler { */ public void setLevel(Level newLevel) { if (newLevel == null) { - throw new NullPointerException(); + throw new NullPointerException("newLevel == null"); } LogManager.getLogManager().checkAccess(); this.level = newLevel; diff --git a/luni/src/main/java/java/util/logging/LogManager.java b/luni/src/main/java/java/util/logging/LogManager.java index 449c263..8877cd5 100644 --- a/luni/src/main/java/java/util/logging/LogManager.java +++ b/luni/src/main/java/java/util/logging/LogManager.java @@ -446,7 +446,7 @@ public class LogManager { */ public void addPropertyChangeListener(PropertyChangeListener l) { if (l == null) { - throw new NullPointerException(); + throw new NullPointerException("l == null"); } checkAccess(); listeners.addPropertyChangeListener(l); diff --git a/luni/src/main/java/java/util/logging/StreamHandler.java b/luni/src/main/java/java/util/logging/StreamHandler.java index 7581835..60b4321 100644 --- a/luni/src/main/java/java/util/logging/StreamHandler.java +++ b/luni/src/main/java/java/util/logging/StreamHandler.java @@ -167,7 +167,7 @@ public class StreamHandler extends Handler { */ protected void setOutputStream(OutputStream os) { if (os == null) { - throw new NullPointerException(); + throw new NullPointerException("os == null"); } LogManager.getLogManager().checkAccess(); close(true); diff --git a/luni/src/main/java/java/util/prefs/AbstractPreferences.java b/luni/src/main/java/java/util/prefs/AbstractPreferences.java index d86d789..71110c3 100644 --- a/luni/src/main/java/java/util/prefs/AbstractPreferences.java +++ b/luni/src/main/java/java/util/prefs/AbstractPreferences.java @@ -372,7 +372,7 @@ public abstract class AbstractPreferences extends Preferences { @Override public void exportNode(OutputStream ostream) throws IOException, BackingStoreException { if (ostream == null) { - throw new NullPointerException("Stream is null"); + throw new NullPointerException("ostream == null"); } checkState(); XMLParser.exportPrefs(this, ostream, false); @@ -381,7 +381,7 @@ public abstract class AbstractPreferences extends Preferences { @Override public void exportSubtree(OutputStream ostream) throws IOException, BackingStoreException { if (ostream == null) { - throw new NullPointerException("Stream is null"); + throw new NullPointerException("ostream == null"); } checkState(); XMLParser.exportPrefs(this, ostream, true); @@ -402,7 +402,7 @@ public abstract class AbstractPreferences extends Preferences { @Override public String get(String key, String deflt) { if (key == null) { - throw new NullPointerException(); + throw new NullPointerException("key == null"); } String result = null; synchronized (lock) { @@ -597,7 +597,7 @@ public abstract class AbstractPreferences extends Preferences { @Override public boolean nodeExists(String name) throws BackingStoreException { if (name == null) { - throw new NullPointerException(); + throw new NullPointerException("name == null"); } AbstractPreferences startNode = null; synchronized (lock) { @@ -640,8 +640,10 @@ public abstract class AbstractPreferences extends Preferences { @Override public void put(String key, String value) { - if (key == null || value == null) { - throw new NullPointerException(); + if (key == null) { + throw new NullPointerException("key == null"); + } else if (value == null) { + throw new NullPointerException("value == null"); } if (key.length() > MAX_KEY_LENGTH || value.length() > MAX_VALUE_LENGTH) { throw new IllegalArgumentException(); @@ -730,7 +732,7 @@ public abstract class AbstractPreferences extends Preferences { @Override public void addNodeChangeListener(NodeChangeListener ncl) { if (ncl == null) { - throw new NullPointerException(); + throw new NullPointerException("ncl == null"); } checkState(); synchronized (nodeChangeListeners) { @@ -741,7 +743,7 @@ public abstract class AbstractPreferences extends Preferences { @Override public void addPreferenceChangeListener(PreferenceChangeListener pcl) { if (pcl == null) { - throw new NullPointerException(); + throw new NullPointerException("pcl == null"); } checkState(); synchronized (preferenceChangeListeners) { diff --git a/luni/src/main/java/java/util/regex/Matcher.java b/luni/src/main/java/java/util/regex/Matcher.java index 58bcf9e..cfd4432 100644 --- a/luni/src/main/java/java/util/regex/Matcher.java +++ b/luni/src/main/java/java/util/regex/Matcher.java @@ -50,11 +50,6 @@ public final class Matcher implements MatchResult { private int regionEnd; /** - * Holds the position where the next find operation will take place. - */ - private int findPos; - - /** * Holds the position where the next append operation will take place. */ private int appendPos; @@ -212,7 +207,6 @@ public final class Matcher implements MatchResult { resetForInput(); matchFound = false; - findPos = regionStart; appendPos = 0; return this; @@ -377,30 +371,17 @@ public final class Matcher implements MatchResult { } /** - * Returns the next occurrence of the {@link Pattern} in the input. The - * method starts the search from the given character in the input. + * Returns true if there is another match in the input, starting + * from the given position. The region is ignored. * - * @param start - * The index in the input at which the find operation is to - * begin. If this is less than the start of the region, it is - * automatically adjusted to that value. If it is beyond the end - * of the region, the method will fail. - * @return true if (and only if) a match has been found. + * @throws IndexOutOfBoundsException if {@code start < 0 || start > input.length()} */ public boolean find(int start) { - findPos = start; - - if (findPos < regionStart) { - findPos = regionStart; - } else if (findPos >= regionEnd) { - matchFound = false; - return false; + if (start < 0 || start > input.length()) { + throw new IndexOutOfBoundsException("start=" + start + "; length=" + input.length()); } - matchFound = findImpl(address, input, findPos, matchOffsets); - if (matchFound) { - findPos = matchOffsets[1]; - } + matchFound = findImpl(address, input, start, matchOffsets); return matchFound; } @@ -414,9 +395,6 @@ public final class Matcher implements MatchResult { */ public boolean find() { matchFound = findNextImpl(address, input, matchOffsets); - if (matchFound) { - findPos = matchOffsets[1]; - } return matchFound; } @@ -429,9 +407,6 @@ public final class Matcher implements MatchResult { */ public boolean lookingAt() { matchFound = lookingAtImpl(address, input, matchOffsets); - if (matchFound) { - findPos = matchOffsets[1]; - } return matchFound; } @@ -444,9 +419,6 @@ public final class Matcher implements MatchResult { */ public boolean matches() { matchFound = matchesImpl(address, input, matchOffsets); - if (matchFound) { - findPos = matchOffsets[1]; - } return matchFound; } diff --git a/luni/src/main/java/java/util/regex/Pattern.java b/luni/src/main/java/java/util/regex/Pattern.java index 46984b9..cbd5965 100644 --- a/luni/src/main/java/java/util/regex/Pattern.java +++ b/luni/src/main/java/java/util/regex/Pattern.java @@ -82,16 +82,23 @@ import java.io.Serializable; * </table> * <p>Most of the time, the built-in character classes are more useful: * <table> - * <tr> <td> \d </td> <td>Any digit character.</td> </tr> - * <tr> <td> \D </td> <td>Any non-digit character.</td> </tr> - * <tr> <td> \s </td> <td>Any whitespace character.</td> </tr> - * <tr> <td> \S </td> <td>Any non-whitespace character.</td> </tr> - * <tr> <td> \w </td> <td>Any word character.</td> </tr> - * <tr> <td> \W </td> <td>Any non-word character.</td> </tr> + * <tr> <td> \d </td> <td>Any digit character (see note below).</td> </tr> + * <tr> <td> \D </td> <td>Any non-digit character (see note below).</td> </tr> + * <tr> <td> \s </td> <td>Any whitespace character (see note below).</td> </tr> + * <tr> <td> \S </td> <td>Any non-whitespace character (see note below).</td> </tr> + * <tr> <td> \w </td> <td>Any word character (see note below).</td> </tr> + * <tr> <td> \W </td> <td>Any non-word character (see note below).</td> </tr> * <tr> <td> \p{<i>NAME</i>} </td> <td> Any character in the class with the given <i>NAME</i>. </td> </tr> * <tr> <td> \P{<i>NAME</i>} </td> <td> Any character <i>not</i> in the named class. </td> </tr> * </table> - * <p>There are a variety of named classes: + * <p>Note that these built-in classes don't just cover the traditional ASCII range. For example, + * <code>\w</code> is equivalent to the character class <code>[\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Nd}]</code>. + * For more details see <a href="http://www.unicode.org/reports/tr18/#Compatibility_Properties">Unicode TR-18</a>, + * and bear in mind that the set of characters in each class can vary between Unicode releases. + * If you actually want to match only ASCII characters, specify the explicit characters you want; + * if you mean 0-9 use <code>[0-9]</code> rather than <code>\d</code>, which would also include + * Gurmukhi digits and so forth. + * <p>There are also a variety of named classes: * <ul> * <li><a href="../../lang/Character.html#unicode_categories">Unicode category names</a>, * prefixed by {@code Is}. For example {@code \p{IsLu}} for all uppercase letters. diff --git a/luni/src/main/java/java/util/zip/DeflaterInputStream.java b/luni/src/main/java/java/util/zip/DeflaterInputStream.java index c6e95f2..805ce17 100644 --- a/luni/src/main/java/java/util/zip/DeflaterInputStream.java +++ b/luni/src/main/java/java/util/zip/DeflaterInputStream.java @@ -72,8 +72,10 @@ public class DeflaterInputStream extends FilterInputStream { */ public DeflaterInputStream(InputStream in, Deflater deflater, int bufferSize) { super(in); - if (in == null || deflater == null) { - throw new NullPointerException(); + if (in == null) { + throw new NullPointerException("in == null"); + } else if (deflater == null) { + throw new NullPointerException("deflater == null"); } if (bufferSize <= 0) { throw new IllegalArgumentException(); diff --git a/luni/src/main/java/java/util/zip/DeflaterOutputStream.java b/luni/src/main/java/java/util/zip/DeflaterOutputStream.java index b0bcb99..d336e72 100644 --- a/luni/src/main/java/java/util/zip/DeflaterOutputStream.java +++ b/luni/src/main/java/java/util/zip/DeflaterOutputStream.java @@ -115,8 +115,10 @@ public class DeflaterOutputStream extends FilterOutputStream { */ public DeflaterOutputStream(OutputStream os, Deflater def, int bsize, boolean syncFlush) { super(os); - if (os == null || def == null) { - throw new NullPointerException(); + if (os == null) { + throw new NullPointerException("os == null"); + } else if (def == null) { + throw new NullPointerException("def == null"); } if (bsize <= 0) { throw new IllegalArgumentException(); diff --git a/luni/src/main/java/java/util/zip/InflaterInputStream.java b/luni/src/main/java/java/util/zip/InflaterInputStream.java index 580d605..6081037 100644 --- a/luni/src/main/java/java/util/zip/InflaterInputStream.java +++ b/luni/src/main/java/java/util/zip/InflaterInputStream.java @@ -103,8 +103,10 @@ public class InflaterInputStream extends FilterInputStream { */ public InflaterInputStream(InputStream is, Inflater inflater, int bsize) { super(is); - if (is == null || inflater == null) { - throw new NullPointerException(); + if (is == null) { + throw new NullPointerException("is == null"); + } else if (inflater == null) { + throw new NullPointerException("inflater == null"); } if (bsize <= 0) { throw new IllegalArgumentException(); diff --git a/luni/src/main/java/java/util/zip/InflaterOutputStream.java b/luni/src/main/java/java/util/zip/InflaterOutputStream.java index c9687b6..9a699a8 100644 --- a/luni/src/main/java/java/util/zip/InflaterOutputStream.java +++ b/luni/src/main/java/java/util/zip/InflaterOutputStream.java @@ -70,8 +70,10 @@ public class InflaterOutputStream extends FilterOutputStream { */ public InflaterOutputStream(OutputStream out, Inflater inf, int bufferSize) { super(out); - if (out == null || inf == null) { - throw new NullPointerException(); + if (out == null) { + throw new NullPointerException("out == null"); + } else if (inf == null) { + throw new NullPointerException("inf == null"); } if (bufferSize <= 0) { throw new IllegalArgumentException(); diff --git a/luni/src/main/java/java/util/zip/ZipEntry.java b/luni/src/main/java/java/util/zip/ZipEntry.java index 988bd2c..e2bfc8d 100644 --- a/luni/src/main/java/java/util/zip/ZipEntry.java +++ b/luni/src/main/java/java/util/zip/ZipEntry.java @@ -71,7 +71,7 @@ public class ZipEntry implements ZipConstants, Cloneable { */ public ZipEntry(String name) { if (name == null) { - throw new NullPointerException(); + throw new NullPointerException("name == null"); } if (name.length() > 0xFFFF) { throw new IllegalArgumentException("Name too long: " + name.length()); diff --git a/luni/src/main/java/java/util/zip/ZipFile.java b/luni/src/main/java/java/util/zip/ZipFile.java index 363f57e..6ecd489 100644 --- a/luni/src/main/java/java/util/zip/ZipFile.java +++ b/luni/src/main/java/java/util/zip/ZipFile.java @@ -223,7 +223,7 @@ public class ZipFile implements ZipConstants { public ZipEntry getEntry(String entryName) { checkNotClosed(); if (entryName == null) { - throw new NullPointerException(); + throw new NullPointerException("entryName == null"); } ZipEntry ze = mEntries.get(entryName); diff --git a/luni/src/main/java/java/util/zip/ZipInputStream.java b/luni/src/main/java/java/util/zip/ZipInputStream.java index d082fc7..e7c4566 100644 --- a/luni/src/main/java/java/util/zip/ZipInputStream.java +++ b/luni/src/main/java/java/util/zip/ZipInputStream.java @@ -101,7 +101,7 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants public ZipInputStream(InputStream stream) { super(new PushbackInputStream(stream, BUF_SIZE), new Inflater(true)); if (stream == null) { - throw new NullPointerException(); + throw new NullPointerException("stream == null"); } } diff --git a/luni/src/main/java/javax/crypto/Cipher.java b/luni/src/main/java/javax/crypto/Cipher.java index 1dacd46..aeb5def 100644 --- a/luni/src/main/java/javax/crypto/Cipher.java +++ b/luni/src/main/java/javax/crypto/Cipher.java @@ -141,10 +141,10 @@ public class Cipher { protected Cipher(CipherSpi cipherSpi, Provider provider, String transformation) { if (cipherSpi == null) { - throw new NullPointerException(); + throw new NullPointerException("cipherSpi == null"); } if (!(cipherSpi instanceof NullCipherSpi) && provider == null) { - throw new NullPointerException(); + throw new NullPointerException("provider == null"); } this.provider = provider; this.transformation = transformation; @@ -1332,7 +1332,7 @@ public class Cipher { public static final int getMaxAllowedKeyLength(String transformation) throws NoSuchAlgorithmException { if (transformation == null) { - throw new NullPointerException(); + throw new NullPointerException("transformation == null"); } checkTransformation(transformation); //FIXME jurisdiction policy files @@ -1356,7 +1356,7 @@ public class Cipher { public static final AlgorithmParameterSpec getMaxAllowedParameterSpec( String transformation) throws NoSuchAlgorithmException { if (transformation == null) { - throw new NullPointerException(); + throw new NullPointerException("transformation == null"); } checkTransformation(transformation); //FIXME jurisdiction policy files diff --git a/luni/src/main/java/javax/crypto/CipherInputStream.java b/luni/src/main/java/javax/crypto/CipherInputStream.java index 39dcfda..a59a425 100644 --- a/luni/src/main/java/javax/crypto/CipherInputStream.java +++ b/luni/src/main/java/javax/crypto/CipherInputStream.java @@ -135,7 +135,7 @@ public class CipherInputStream extends FilterInputStream { @Override public int read(byte[] buf, int off, int len) throws IOException { if (in == null) { - throw new NullPointerException("Underlying input stream is null"); + throw new NullPointerException("in == null"); } int i; diff --git a/luni/src/main/java/javax/crypto/EncryptedPrivateKeyInfo.java b/luni/src/main/java/javax/crypto/EncryptedPrivateKeyInfo.java index 034f07a..0fb5b76 100644 --- a/luni/src/main/java/javax/crypto/EncryptedPrivateKeyInfo.java +++ b/luni/src/main/java/javax/crypto/EncryptedPrivateKeyInfo.java @@ -121,7 +121,7 @@ public class EncryptedPrivateKeyInfo { * Creates an {@code EncryptedPrivateKeyInfo} instance from an algorithm * name and its encrypted data. * - * @param encrAlgName + * @param encryptionAlgorithmName * the name of an algorithm. * @param encryptedData * the encrypted data. @@ -133,12 +133,12 @@ public class EncryptedPrivateKeyInfo { * @throws IllegalArgumentException * if {@code encryptedData} is empty. */ - public EncryptedPrivateKeyInfo(String encrAlgName, byte[] encryptedData) + public EncryptedPrivateKeyInfo(String encryptionAlgorithmName, byte[] encryptedData) throws NoSuchAlgorithmException { - if (encrAlgName == null) { - throw new NullPointerException("the algName parameter is null"); + if (encryptionAlgorithmName == null) { + throw new NullPointerException("encryptionAlgorithmName == null"); } - this.algName = encrAlgName; + this.algName = encryptionAlgorithmName; if (!mapAlgName()) { throw new NoSuchAlgorithmException("Unsupported algorithm: " + this.algName); } diff --git a/luni/src/main/java/javax/crypto/ExemptionMechanism.java b/luni/src/main/java/javax/crypto/ExemptionMechanism.java index 2ac4994..8745b78 100644 --- a/luni/src/main/java/javax/crypto/ExemptionMechanism.java +++ b/luni/src/main/java/javax/crypto/ExemptionMechanism.java @@ -98,7 +98,7 @@ public class ExemptionMechanism { public static final ExemptionMechanism getInstance(String algorithm) throws NoSuchAlgorithmException { if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null); return new ExemptionMechanism((ExemptionMechanismSpi) sap.spi, sap.provider, algorithm); @@ -134,7 +134,7 @@ public class ExemptionMechanism { throw new NoSuchProviderException(provider); } if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } return getInstance(algorithm, impProvider); } @@ -159,7 +159,7 @@ public class ExemptionMechanism { public static final ExemptionMechanism getInstance(String algorithm, Provider provider) throws NoSuchAlgorithmException { if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } if (provider == null) { throw new IllegalArgumentException("provider == null"); diff --git a/luni/src/main/java/javax/crypto/KeyAgreement.java b/luni/src/main/java/javax/crypto/KeyAgreement.java index 9c5d86c..51b4cd1 100644 --- a/luni/src/main/java/javax/crypto/KeyAgreement.java +++ b/luni/src/main/java/javax/crypto/KeyAgreement.java @@ -99,7 +99,7 @@ public class KeyAgreement { public static final KeyAgreement getInstance(String algorithm) throws NoSuchAlgorithmException { if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null); return new KeyAgreement((KeyAgreementSpi) sap.spi, sap.provider, algorithm); @@ -161,7 +161,7 @@ public class KeyAgreement { throw new IllegalArgumentException("provider == null"); } if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } Object spi = ENGINE.getInstance(algorithm, provider, null); return new KeyAgreement((KeyAgreementSpi) spi, provider, algorithm); diff --git a/luni/src/main/java/javax/crypto/KeyGenerator.java b/luni/src/main/java/javax/crypto/KeyGenerator.java index 77b1a82..606998a 100644 --- a/luni/src/main/java/javax/crypto/KeyGenerator.java +++ b/luni/src/main/java/javax/crypto/KeyGenerator.java @@ -98,7 +98,7 @@ public class KeyGenerator { public static final KeyGenerator getInstance(String algorithm) throws NoSuchAlgorithmException { if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null); return new KeyGenerator((KeyGeneratorSpi) sap.spi, sap.provider, algorithm); @@ -158,7 +158,7 @@ public class KeyGenerator { throw new IllegalArgumentException("provider == null"); } if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } Object spi = ENGINE.getInstance(algorithm, provider, null); return new KeyGenerator((KeyGeneratorSpi) spi, provider, algorithm); diff --git a/luni/src/main/java/javax/crypto/Mac.java b/luni/src/main/java/javax/crypto/Mac.java index 1a05b59..46be141 100644 --- a/luni/src/main/java/javax/crypto/Mac.java +++ b/luni/src/main/java/javax/crypto/Mac.java @@ -101,7 +101,7 @@ public class Mac implements Cloneable { public static final Mac getInstance(String algorithm) throws NoSuchAlgorithmException { if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null); return new Mac((MacSpi) sap.spi, sap.provider, algorithm); @@ -163,7 +163,7 @@ public class Mac implements Cloneable { throw new IllegalArgumentException("provider == null"); } if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } Object spi = ENGINE.getInstance(algorithm, provider, null); return new Mac((MacSpi) spi, provider, algorithm); diff --git a/luni/src/main/java/javax/crypto/SealedObject.java b/luni/src/main/java/javax/crypto/SealedObject.java index c9c1534..cfb970b 100644 --- a/luni/src/main/java/javax/crypto/SealedObject.java +++ b/luni/src/main/java/javax/crypto/SealedObject.java @@ -33,12 +33,12 @@ import java.security.NoSuchProviderException; /** * A {@code SealedObject} is a wrapper around a {@code serializable} object * instance and encrypts it using a cryptographic cipher. - * <p> - * Since a {@code SealedObject} instance is a serializable object itself it can + * + * <p>Since a {@code SealedObject} instance is serializable it can * either be stored or transmitted over an insecure channel. - * <p> - * The wrapped object can later be decrypted (unsealed) using the corresponding - * key and then be deserialized to retrieve the original object.The sealed + * + * <p>The wrapped object can later be decrypted (unsealed) using the corresponding + * key and then be deserialized to retrieve the original object. The sealed * object itself keeps track of the cipher and corresponding parameters. */ public class SealedObject implements Serializable { @@ -46,19 +46,25 @@ public class SealedObject implements Serializable { private static final long serialVersionUID = 4482838265551344752L; /** - * The {@link AlgorithmParameters} in encoded format. + * The cipher's {@link AlgorithmParameters} in encoded format. + * Equivalent to {@code cipher.getParameters().getEncoded()}, + * or null if the cipher did not use any parameters. */ protected byte[] encodedParams; + private byte[] encryptedContent; private String sealAlg; private String paramsAlg; - private void readObject(ObjectInputStream s) - throws IOException, ClassNotFoundException { - encodedParams = (byte []) s.readUnshared(); - encryptedContent = (byte []) s.readUnshared(); - sealAlg = (String) s.readUnshared(); - paramsAlg = (String) s.readUnshared(); + private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { + // We do unshared reads here to ensure we have our own clones of the byte[]s. + encodedParams = (byte[]) s.readUnshared(); + encryptedContent = (byte[]) s.readUnshared(); + // These are regular shared reads because the algorithms used by a given stream are + // almost certain to the be same for each object, and String is immutable anyway, + // so there's no security concern about sharing. + sealAlg = (String) s.readObject(); + paramsAlg = (String) s.readObject(); } /** diff --git a/luni/src/main/java/javax/crypto/SecretKeyFactory.java b/luni/src/main/java/javax/crypto/SecretKeyFactory.java index 5e47abe..8ab3eb8 100644 --- a/luni/src/main/java/javax/crypto/SecretKeyFactory.java +++ b/luni/src/main/java/javax/crypto/SecretKeyFactory.java @@ -103,7 +103,7 @@ public class SecretKeyFactory { public static final SecretKeyFactory getInstance(String algorithm) throws NoSuchAlgorithmException { if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null); return new SecretKeyFactory((SecretKeyFactorySpi) sap.spi, sap.provider, algorithm); @@ -165,7 +165,7 @@ public class SecretKeyFactory { throw new IllegalArgumentException("provider == null"); } if (algorithm == null) { - throw new NullPointerException(); + throw new NullPointerException("algorithm == null"); } Object spi = ENGINE.getInstance(algorithm, provider, null); return new SecretKeyFactory((SecretKeyFactorySpi) spi, provider, algorithm); diff --git a/luni/src/main/java/javax/crypto/spec/DESedeKeySpec.java b/luni/src/main/java/javax/crypto/spec/DESedeKeySpec.java index fe86aeb..fcfe749 100644 --- a/luni/src/main/java/javax/crypto/spec/DESedeKeySpec.java +++ b/luni/src/main/java/javax/crypto/spec/DESedeKeySpec.java @@ -45,7 +45,7 @@ public class DESedeKeySpec implements KeySpec { */ public DESedeKeySpec(byte[] key) throws InvalidKeyException { if (key == null) { - throw new NullPointerException(); + throw new NullPointerException("key == null"); } if (key.length < DES_EDE_KEY_LEN) { throw new InvalidKeyException(); @@ -71,7 +71,7 @@ public class DESedeKeySpec implements KeySpec { */ public DESedeKeySpec(byte[] key, int offset) throws InvalidKeyException { if (key == null) { - throw new NullPointerException(); + throw new NullPointerException("key == null"); } if (key.length - offset < DES_EDE_KEY_LEN) { throw new InvalidKeyException(); diff --git a/luni/src/main/java/javax/crypto/spec/OAEPParameterSpec.java b/luni/src/main/java/javax/crypto/spec/OAEPParameterSpec.java index 3bc9ab4..340e57f 100644 --- a/luni/src/main/java/javax/crypto/spec/OAEPParameterSpec.java +++ b/luni/src/main/java/javax/crypto/spec/OAEPParameterSpec.java @@ -73,8 +73,12 @@ public class OAEPParameterSpec implements AlgorithmParameterSpec { */ public OAEPParameterSpec(String mdName, String mgfName, AlgorithmParameterSpec mgfSpec, PSource pSrc) { - if ((mdName == null) || (mgfName == null) || (pSrc == null)) { - throw new NullPointerException(); + if (mdName == null) { + throw new NullPointerException("mdName == null"); + } else if (mgfName == null) { + throw new NullPointerException("mgfName == null"); + } else if (pSrc == null) { + throw new NullPointerException("pSrc == null"); } this.mdName = mdName; this.mgfName = mgfName; diff --git a/luni/src/main/java/javax/crypto/spec/PSource.java b/luni/src/main/java/javax/crypto/spec/PSource.java index 0efa1e9..f644316 100644 --- a/luni/src/main/java/javax/crypto/spec/PSource.java +++ b/luni/src/main/java/javax/crypto/spec/PSource.java @@ -40,7 +40,7 @@ public class PSource { */ protected PSource(String pSrcName) { if (pSrcName == null) { - throw new NullPointerException(); + throw new NullPointerException("pSrcName == null"); } this.pSrcName = pSrcName; } @@ -85,7 +85,7 @@ public class PSource { public PSpecified(byte[] p) { super("PSpecified"); if (p == null) { - throw new NullPointerException(); + throw new NullPointerException("p == null"); } //TODO: It is unknown which name should be used! //super(""); diff --git a/luni/src/main/java/javax/net/ssl/KeyManagerFactory.java b/luni/src/main/java/javax/net/ssl/KeyManagerFactory.java index 82ce8a1..0b3db61 100644 --- a/luni/src/main/java/javax/net/ssl/KeyManagerFactory.java +++ b/luni/src/main/java/javax/net/ssl/KeyManagerFactory.java @@ -68,7 +68,7 @@ public class KeyManagerFactory { public static final KeyManagerFactory getInstance(String algorithm) throws NoSuchAlgorithmException { if (algorithm == null) { - throw new NullPointerException("algorithm is null"); + throw new NullPointerException("algorithm == null"); } Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null); return new KeyManagerFactory((KeyManagerFactorySpi) sap.spi, sap.provider, algorithm); @@ -127,7 +127,7 @@ public class KeyManagerFactory { throw new IllegalArgumentException("Provider is null"); } if (algorithm == null) { - throw new NullPointerException("algorithm is null"); + throw new NullPointerException("algorithm == null"); } Object spi = ENGINE.getInstance(algorithm, provider, null); return new KeyManagerFactory((KeyManagerFactorySpi) spi, provider, algorithm); diff --git a/luni/src/main/java/javax/net/ssl/SSLContext.java b/luni/src/main/java/javax/net/ssl/SSLContext.java index 9097bb9..a59f301 100644 --- a/luni/src/main/java/javax/net/ssl/SSLContext.java +++ b/luni/src/main/java/javax/net/ssl/SSLContext.java @@ -93,7 +93,7 @@ public class SSLContext { */ public static SSLContext getInstance(String protocol) throws NoSuchAlgorithmException { if (protocol == null) { - throw new NullPointerException("protocol is null"); + throw new NullPointerException("protocol == null"); } Engine.SpiAndProvider sap = ENGINE.getInstance(protocol, null); return new SSLContext((SSLContextSpi) sap.spi, sap.provider, protocol); @@ -154,7 +154,7 @@ public class SSLContext { throw new IllegalArgumentException("provider is null"); } if (protocol == null) { - throw new NullPointerException("protocol is null"); + throw new NullPointerException("protocol == null"); } Object spi = ENGINE.getInstance(protocol, provider, null); return new SSLContext((SSLContextSpi) spi, provider, protocol); diff --git a/luni/src/main/java/javax/net/ssl/TrustManagerFactory.java b/luni/src/main/java/javax/net/ssl/TrustManagerFactory.java index bf3bf8c..be9db06 100644 --- a/luni/src/main/java/javax/net/ssl/TrustManagerFactory.java +++ b/luni/src/main/java/javax/net/ssl/TrustManagerFactory.java @@ -67,7 +67,7 @@ public class TrustManagerFactory { public static final TrustManagerFactory getInstance(String algorithm) throws NoSuchAlgorithmException { if (algorithm == null) { - throw new NullPointerException("algorithm is null"); + throw new NullPointerException("algorithm == null"); } Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null); return new TrustManagerFactory((TrustManagerFactorySpi) sap.spi, sap.provider, algorithm); @@ -126,7 +126,7 @@ public class TrustManagerFactory { throw new IllegalArgumentException("Provider is null"); } if (algorithm == null) { - throw new NullPointerException("algorithm is null"); + throw new NullPointerException("algorithm == null"); } Object spi = ENGINE.getInstance(algorithm, provider, null); return new TrustManagerFactory((TrustManagerFactorySpi) spi, provider, algorithm); diff --git a/luni/src/main/java/javax/security/auth/Subject.java b/luni/src/main/java/javax/security/auth/Subject.java index a958484..6c9c036 100644 --- a/luni/src/main/java/javax/security/auth/Subject.java +++ b/luni/src/main/java/javax/security/auth/Subject.java @@ -116,8 +116,12 @@ public final class Subject implements Serializable { public Subject(boolean readOnly, Set<? extends Principal> subjPrincipals, Set<?> pubCredentials, Set<?> privCredentials) { - if (subjPrincipals == null || pubCredentials == null || privCredentials == null) { - throw new NullPointerException(); + if (subjPrincipals == null) { + throw new NullPointerException("subjPrincipals == null"); + } else if (pubCredentials == null) { + throw new NullPointerException("pubCredentials == null"); + } else if (privCredentials == null) { + throw new NullPointerException("privCredentials == null"); } principals = new SecureSet<Principal>(_PRINCIPALS, subjPrincipals); @@ -467,7 +471,7 @@ public final class Subject implements Serializable { */ public static Subject getSubject(final AccessControlContext context) { if (context == null) { - throw new NullPointerException("AccessControlContext cannot be null"); + throw new NullPointerException("context == null"); } PrivilegedAction<DomainCombiner> action = new PrivilegedAction<DomainCombiner>() { public DomainCombiner run() { @@ -554,7 +558,7 @@ public final class Subject implements Serializable { private void verifyElement(Object o) { if (o == null) { - throw new NullPointerException(); + throw new NullPointerException("o == null"); } if (permission == _PRINCIPALS && !(Principal.class.isAssignableFrom(o.getClass()))) { throw new IllegalArgumentException("Element is not instance of java.security.Principal"); @@ -607,7 +611,7 @@ public final class Subject implements Serializable { public boolean retainAll(Collection<?> c) { if (c == null) { - throw new NullPointerException(); + throw new NullPointerException("c == null"); } return super.retainAll(c); } @@ -624,7 +628,7 @@ public final class Subject implements Serializable { protected final <E> Set<E> get(final Class<E> c) { if (c == null) { - throw new NullPointerException(); + throw new NullPointerException("c == null"); } AbstractSet<E> s = new AbstractSet<E>() { @@ -652,7 +656,7 @@ public final class Subject implements Serializable { public boolean retainAll(Collection<?> c) { if (c == null) { - throw new NullPointerException(); + throw new NullPointerException("c == null"); } return super.retainAll(c); } diff --git a/luni/src/main/java/javax/security/auth/x500/X500Principal.java b/luni/src/main/java/javax/security/auth/x500/X500Principal.java index e6453e9..cedebe0 100644 --- a/luni/src/main/java/javax/security/auth/x500/X500Principal.java +++ b/luni/src/main/java/javax/security/auth/x500/X500Principal.java @@ -123,7 +123,7 @@ public final class X500Principal implements Serializable, Principal { */ public X500Principal(String name) { if (name == null) { - throw new NullPointerException("Name cannot be null"); + throw new NullPointerException("name == null"); } try { dn = new Name(name); @@ -134,7 +134,7 @@ public final class X500Principal implements Serializable, Principal { public X500Principal(String name, Map<String,String> keywordMap){ if (name == null) { - throw new NullPointerException("Name cannot be null"); + throw new NullPointerException("name == null"); } try { dn = new Name(substituteNameFromMap(name, keywordMap)); diff --git a/luni/src/main/java/javax/xml/datatype/DatatypeFactory.java b/luni/src/main/java/javax/xml/datatype/DatatypeFactory.java index 6a89dae..68291b6 100644 --- a/luni/src/main/java/javax/xml/datatype/DatatypeFactory.java +++ b/luni/src/main/java/javax/xml/datatype/DatatypeFactory.java @@ -326,7 +326,7 @@ public abstract class DatatypeFactory { */ public Duration newDurationDayTime(final String lexicalRepresentation) { if (lexicalRepresentation == null) { - throw new NullPointerException("The lexical representation cannot be null."); + throw new NullPointerException("lexicalRepresentation == null"); } // The lexical representation must match the pattern [^YM]*(T.*)? int pos = lexicalRepresentation.indexOf('T'); @@ -539,7 +539,7 @@ public abstract class DatatypeFactory { */ public Duration newDurationYearMonth(final String lexicalRepresentation) { if (lexicalRepresentation == null) { - throw new NullPointerException("The lexical representation cannot be null."); + throw new NullPointerException("lexicalRepresentation == null"); } // The lexical representation must match the pattern [^DT]*. int length = lexicalRepresentation.length(); diff --git a/luni/src/main/java/javax/xml/datatype/Duration.java b/luni/src/main/java/javax/xml/datatype/Duration.java index 8121d36..fcdd4c5 100644 --- a/luni/src/main/java/javax/xml/datatype/Duration.java +++ b/luni/src/main/java/javax/xml/datatype/Duration.java @@ -552,11 +552,7 @@ public abstract class Duration { // check data parameter if (date == null) { - throw new NullPointerException( - "Cannot call " - + this.getClass().getName() - + "#addTo(Date date) with date == null." - ); + throw new NullPointerException("date == null"); } Calendar cal = new GregorianCalendar(); diff --git a/luni/src/main/java/javax/xml/namespace/QName.java b/luni/src/main/java/javax/xml/namespace/QName.java index f748b64..a82487f 100644 --- a/luni/src/main/java/javax/xml/namespace/QName.java +++ b/luni/src/main/java/javax/xml/namespace/QName.java @@ -22,8 +22,6 @@ package javax.xml.namespace; import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; -import java.security.AccessController; -import java.security.PrivilegedAction; import javax.xml.XMLConstants; /** diff --git a/luni/src/main/java/javax/xml/validation/SchemaFactory.java b/luni/src/main/java/javax/xml/validation/SchemaFactory.java index 23e4798..2018067 100644 --- a/luni/src/main/java/javax/xml/validation/SchemaFactory.java +++ b/luni/src/main/java/javax/xml/validation/SchemaFactory.java @@ -203,8 +203,10 @@ public abstract class SchemaFactory { */ public static SchemaFactory newInstance(String schemaLanguage, String factoryClassName, ClassLoader classLoader) { - if (schemaLanguage == null || factoryClassName == null) { - throw new NullPointerException("schemaLanguage == null || factoryClassName == null"); + if (schemaLanguage == null) { + throw new NullPointerException("schemaLanguage == null"); + } else if (factoryClassName == null) { + throw new NullPointerException("factoryClassName == null"); } if (classLoader == null) { classLoader = Thread.currentThread().getContextClassLoader(); @@ -265,7 +267,7 @@ public abstract class SchemaFactory { public boolean getFeature(String name) throws SAXNotRecognizedException, SAXNotSupportedException { if (name == null) { - throw new NullPointerException("the name parameter is null"); + throw new NullPointerException("name == null"); } throw new SAXNotRecognizedException(name); } @@ -313,7 +315,7 @@ public abstract class SchemaFactory { */ public void setFeature(String name, boolean value) throws SAXNotRecognizedException, SAXNotSupportedException { if (name == null) { - throw new NullPointerException("the name parameter is null"); + throw new NullPointerException("name == null"); } throw new SAXNotRecognizedException(name); } @@ -340,7 +342,7 @@ public abstract class SchemaFactory { */ public void setProperty(String name, Object object) throws SAXNotRecognizedException, SAXNotSupportedException { if (name == null) { - throw new NullPointerException("the name parameter is null"); + throw new NullPointerException("name == null"); } throw new SAXNotRecognizedException(name); } @@ -371,7 +373,7 @@ public abstract class SchemaFactory { */ public Object getProperty(String name) throws SAXNotRecognizedException, SAXNotSupportedException { if (name == null) { - throw new NullPointerException("the name parameter is null"); + throw new NullPointerException("name == null"); } throw new SAXNotRecognizedException(name); } diff --git a/luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java b/luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java index 3a6cb83..636777c 100644 --- a/luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java +++ b/luni/src/main/java/javax/xml/validation/SchemaFactoryFinder.java @@ -131,7 +131,9 @@ final class SchemaFactoryFinder { * If the <tt>schemaLanguage</tt> parameter is null. */ public SchemaFactory newFactory(String schemaLanguage) { - if(schemaLanguage==null) throw new NullPointerException(); + if (schemaLanguage == null) { + throw new NullPointerException("schemaLanguage == null"); + } SchemaFactory f = _newFactory(schemaLanguage); if (debug) { if (f != null) { diff --git a/luni/src/main/java/javax/xml/validation/Validator.java b/luni/src/main/java/javax/xml/validation/Validator.java index b4ee1ca..ea7908a 100644 --- a/luni/src/main/java/javax/xml/validation/Validator.java +++ b/luni/src/main/java/javax/xml/validation/Validator.java @@ -339,7 +339,9 @@ public abstract class Validator { * @see #setFeature(String, boolean) */ public boolean getFeature(String name) throws SAXNotRecognizedException, SAXNotSupportedException { - if(name==null) throw new NullPointerException("the name parameter is null"); + if (name == null) { + throw new NullPointerException("name == null"); + } throw new SAXNotRecognizedException(name); } @@ -372,7 +374,9 @@ public abstract class Validator { * @see #getFeature(String) */ public void setFeature(String name, boolean value) throws SAXNotRecognizedException, SAXNotSupportedException { - if(name==null) throw new NullPointerException("the name parameter is null"); + if (name == null) { + throw new NullPointerException("name == null"); + } throw new SAXNotRecognizedException(name); } @@ -400,7 +404,9 @@ public abstract class Validator { * When the name parameter is null. */ public void setProperty(String name, Object object) throws SAXNotRecognizedException, SAXNotSupportedException { - if(name==null) throw new NullPointerException("the name parameter is null"); + if (name == null) { + throw new NullPointerException("name == null"); + } throw new SAXNotRecognizedException(name); } @@ -431,7 +437,9 @@ public abstract class Validator { * @see #setProperty(String, Object) */ public Object getProperty(String name) throws SAXNotRecognizedException, SAXNotSupportedException { - if(name==null) throw new NullPointerException("the name parameter is null"); + if (name == null) { + throw new NullPointerException("name == null"); + } throw new SAXNotRecognizedException(name); } } diff --git a/luni/src/main/java/javax/xml/validation/ValidatorHandler.java b/luni/src/main/java/javax/xml/validation/ValidatorHandler.java index 9606193..2b621ff 100644 --- a/luni/src/main/java/javax/xml/validation/ValidatorHandler.java +++ b/luni/src/main/java/javax/xml/validation/ValidatorHandler.java @@ -347,8 +347,9 @@ public abstract class ValidatorHandler implements ContentHandler { * @see #setFeature(String, boolean) */ public boolean getFeature(String name) throws SAXNotRecognizedException, SAXNotSupportedException { - if(name==null) - throw new NullPointerException(); + if (name == null) { + throw new NullPointerException("name == null"); + } throw new SAXNotRecognizedException(name); } @@ -381,8 +382,9 @@ public abstract class ValidatorHandler implements ContentHandler { * @see #getFeature(String) */ public void setFeature(String name, boolean value) throws SAXNotRecognizedException, SAXNotSupportedException { - if(name==null) - throw new NullPointerException(); + if (name == null) { + throw new NullPointerException("name == null"); + } throw new SAXNotRecognizedException(name); } @@ -411,8 +413,9 @@ public abstract class ValidatorHandler implements ContentHandler { * When the name parameter is null. */ public void setProperty(String name, Object object) throws SAXNotRecognizedException, SAXNotSupportedException { - if(name==null) - throw new NullPointerException(); + if (name == null) { + throw new NullPointerException("name == null"); + } throw new SAXNotRecognizedException(name); } @@ -443,8 +446,9 @@ public abstract class ValidatorHandler implements ContentHandler { * @see #setProperty(String, Object) */ public Object getProperty(String name) throws SAXNotRecognizedException, SAXNotSupportedException { - if(name==null) - throw new NullPointerException(); + if (name == null) { + throw new NullPointerException("name == null"); + } throw new SAXNotRecognizedException(name); } } diff --git a/luni/src/main/java/javax/xml/xpath/XPathException.java b/luni/src/main/java/javax/xml/xpath/XPathException.java index 8db369b..376d477 100644 --- a/luni/src/main/java/javax/xml/xpath/XPathException.java +++ b/luni/src/main/java/javax/xml/xpath/XPathException.java @@ -48,8 +48,8 @@ public class XPathException extends Exception { */ public XPathException(String message) { super(message); - if ( message == null ) { - throw new NullPointerException ( "message can't be null"); + if (message == null) { + throw new NullPointerException("message == null"); } this.cause = null; } @@ -66,8 +66,8 @@ public class XPathException extends Exception { public XPathException(Throwable cause) { super(cause == null ? null : cause.toString()); this.cause = cause; - if ( cause == null ) { - throw new NullPointerException ( "cause can't be null"); + if (cause == null) { + throw new NullPointerException("cause == null"); } } diff --git a/luni/src/main/java/javax/xml/xpath/XPathFactory.java b/luni/src/main/java/javax/xml/xpath/XPathFactory.java index 8b1c1fa..57f2195 100644 --- a/luni/src/main/java/javax/xml/xpath/XPathFactory.java +++ b/luni/src/main/java/javax/xml/xpath/XPathFactory.java @@ -133,9 +133,7 @@ public abstract class XPathFactory { public static final XPathFactory newInstance(final String uri) throws XPathFactoryConfigurationException { if (uri == null) { - throw new NullPointerException( - "XPathFactory#newInstance(String uri) cannot be called with uri == null" - ); + throw new NullPointerException("uri == null"); } if (uri.length() == 0) { throw new IllegalArgumentException( @@ -167,9 +165,7 @@ public abstract class XPathFactory { public static XPathFactory newInstance(String uri, String factoryClassName, ClassLoader classLoader) throws XPathFactoryConfigurationException { if (uri == null) { - throw new NullPointerException( - "XPathFactory#newInstance(String uri) cannot be called with uri == null" - ); + throw new NullPointerException("uri == null"); } if (uri.length() == 0) { throw new IllegalArgumentException( diff --git a/luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java b/luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java index 652a1d8..0113e7d 100644 --- a/luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java +++ b/luni/src/main/java/javax/xml/xpath/XPathFactoryFinder.java @@ -126,7 +126,9 @@ final class XPathFactoryFinder { * If the parameter is null. */ public XPathFactory newFactory(String uri) { - if(uri==null) throw new NullPointerException(); + if (uri == null) { + throw new NullPointerException("uri == null"); + } XPathFactory f = _newFactory(uri); if (debug) { if (f != null) { diff --git a/luni/src/main/java/javax/xml/xpath/package.html b/luni/src/main/java/javax/xml/xpath/package.html index edeb448..202a29c 100644 --- a/luni/src/main/java/javax/xml/xpath/package.html +++ b/luni/src/main/java/javax/xml/xpath/package.html @@ -165,7 +165,7 @@ or more nodes from an XML document:</p> XPath xpath = XPathFactory.newInstance().newXPath(); String expression = "/widgets/widget"; InputSource inputSource = new InputSource("widgets.xml"); -NodeSet nodes = (NodeSet) xpath.evaluate(expression, inputSource, XPathConstants.NODESET); +NodeList nodes = (NodeList) xpath.evaluate(expression, inputSource, XPathConstants.NODESET); </pre> <h3>XPath Expressions and Types</h3> diff --git a/luni/src/main/java/libcore/icu/ErrorCode.java b/luni/src/main/java/libcore/icu/ErrorCode.java deleted file mode 100644 index c093af2..0000000 --- a/luni/src/main/java/libcore/icu/ErrorCode.java +++ /dev/null @@ -1,71 +0,0 @@ -/** -****************************************************************************** -* Copyright (C) 1996-2005, International Business Machines Corporation and * -* others. All Rights Reserved. * -****************************************************************************** -* -****************************************************************************** -*/ - -package libcore.icu; - -/** - * Error exception class mapping ICU error codes of the enum UErrorCode - * @author syn wee quek -*/ -public final class ErrorCode extends Exception { - public static boolean isFailure(int error) { - return error > U_ZERO_ERROR && error < U_ERROR_LIMIT; - } - - public static RuntimeException throwException(int error) { - if (error <= U_ZERO_ERROR && error >= U_ERROR_LIMIT) { - return null; - } - switch (error) { - case U_ILLEGAL_ARGUMENT_ERROR: - return new IllegalArgumentException(ERROR_NAMES[error]); - case U_INDEX_OUTOFBOUNDS_ERROR: - case U_BUFFER_OVERFLOW_ERROR: - return new ArrayIndexOutOfBoundsException(ERROR_NAMES[error]); - case U_UNSUPPORTED_ERROR: - return new UnsupportedOperationException(ERROR_NAMES[error]); - } - throw new RuntimeException(ERROR_NAMES[error]); - } - - // The errors needed by our CharsetDecoderICU/CharsetEncoderICU. - public static final int U_ZERO_ERROR = 0; - private static final int U_ILLEGAL_ARGUMENT_ERROR = 1; - private static final int U_INDEX_OUTOFBOUNDS_ERROR = 8; - public static final int U_INVALID_CHAR_FOUND = 10; - public static final int U_TRUNCATED_CHAR_FOUND = 11; - public static final int U_ILLEGAL_CHAR_FOUND = 12; - public static final int U_BUFFER_OVERFLOW_ERROR = 15; - private static final int U_UNSUPPORTED_ERROR = 16; - private static final int U_ERROR_LIMIT = 21; - - // TODO: this list is incomplete; get these from native code! - private static final String ERROR_NAMES[] = { - "U_ZERO_ERROR", - "U_ILLEGAL_ARGUMENT_ERROR", - "U_MISSING_RESOURCE_ERROR", - "U_INVALID_FORMAT_ERROR", - "U_FILE_ACCESS_ERROR", - "U_INTERNAL_PROGRAM_ERROR", - "U_MESSAGE_PARSE_ERROR", - "U_MEMORY_ALLOCATION_ERROR", - "U_INDEX_OUTOFBOUNDS_ERROR", - "U_PARSE_ERROR", - "U_INVALID_CHAR_FOUND", - "U_TRUNCATED_CHAR_FOUND", - "U_ILLEGAL_CHAR_FOUND", - "U_INVALID_TABLE_FORMAT", - "U_INVALID_TABLE_FILE", - "U_BUFFER_OVERFLOW_ERROR", - "U_UNSUPPORTED_ERROR", - "U_RESOURCE_TYPE_MISMATCH", - "U_ILLEGAL_ESCAPE_SEQUENCE", - "U_UNSUPPORTED_ESCAPE_SEQUENCE" - }; -} diff --git a/luni/src/main/java/libcore/icu/ICU.java b/luni/src/main/java/libcore/icu/ICU.java index 7f3303e..9984414 100644 --- a/luni/src/main/java/libcore/icu/ICU.java +++ b/luni/src/main/java/libcore/icu/ICU.java @@ -142,6 +142,19 @@ public final class ICU { public static native String toLowerCase(String s, String localeName); public static native String toUpperCase(String s, String localeName); + // --- Errors. + + // Just the subset of error codes needed by CharsetDecoderICU/CharsetEncoderICU. + public static final int U_ZERO_ERROR = 0; + public static final int U_INVALID_CHAR_FOUND = 10; + public static final int U_TRUNCATED_CHAR_FOUND = 11; + public static final int U_ILLEGAL_CHAR_FOUND = 12; + public static final int U_BUFFER_OVERFLOW_ERROR = 15; + + public static boolean U_FAILURE(int error) { + return error > U_ZERO_ERROR; + } + // --- Native methods accessing ICU's database. private static native String[] getAvailableBreakIteratorLocalesNative(); diff --git a/luni/src/main/java/libcore/icu/LocaleData.java b/luni/src/main/java/libcore/icu/LocaleData.java index cb9c880..8ec2294 100644 --- a/luni/src/main/java/libcore/icu/LocaleData.java +++ b/luni/src/main/java/libcore/icu/LocaleData.java @@ -20,6 +20,7 @@ import java.text.DateFormat; import java.util.Arrays; import java.util.HashMap; import java.util.Locale; +import libcore.util.Objects; /** * Passes locale-specific from ICU native code to Java. @@ -46,18 +47,27 @@ public final class LocaleData { public Integer minimalDaysInFirstWeek; // Used by DateFormatSymbols. - public String[] amPm; - public String[] eras; - - public String[] longMonthNames; - public String[] shortMonthNames; - public String[] longStandAloneMonthNames; - public String[] shortStandAloneMonthNames; - - public String[] longWeekdayNames; - public String[] shortWeekdayNames; - public String[] longStandAloneWeekdayNames; - public String[] shortStandAloneWeekdayNames; + public String[] amPm; // "AM", "PM". + public String[] eras; // "BC", "AD". + + public String[] longMonthNames; // "January", ... + public String[] shortMonthNames; // "Jan", ... + public String[] tinyMonthNames; // "J", ... + public String[] longStandAloneMonthNames; // "January", ... + public String[] shortStandAloneMonthNames; // "Jan", ... + public String[] tinyStandAloneMonthNames; // "J", ... + + public String[] longWeekdayNames; // "Sunday", ... + public String[] shortWeekdayNames; // "Sun", ... + public String[] tinyWeekdayNames; // "S", ... + public String[] longStandAloneWeekdayNames; // "Sunday", ... + public String[] shortStandAloneWeekdayNames; // "Sun", ... + public String[] tinyStandAloneWeekdayNames; // "S", ... + + // Used by frameworks/base DateSorter and DateUtils. + public String yesterday; // "Yesterday". + public String today; // "Today". + public String tomorrow; // "Tomorrow". public String fullTimeFormat; public String longTimeFormat; @@ -120,44 +130,7 @@ public final class LocaleData { } @Override public String toString() { - return "LocaleData[" + - "firstDayOfWeek=" + firstDayOfWeek + "," + - "minimalDaysInFirstWeek=" + minimalDaysInFirstWeek + "," + - "amPm=" + Arrays.toString(amPm) + "," + - "eras=" + Arrays.toString(eras) + "," + - "longMonthNames=" + Arrays.toString(longMonthNames) + "," + - "shortMonthNames=" + Arrays.toString(shortMonthNames) + "," + - "longStandAloneMonthNames=" + Arrays.toString(longStandAloneMonthNames) + "," + - "shortStandAloneMonthNames=" + Arrays.toString(shortStandAloneMonthNames) + "," + - "longWeekdayNames=" + Arrays.toString(longWeekdayNames) + "," + - "shortWeekdayNames=" + Arrays.toString(shortWeekdayNames) + "," + - "longStandAloneWeekdayNames=" + Arrays.toString(longStandAloneWeekdayNames) + "," + - "shortStandAloneWeekdayNames=" + Arrays.toString(shortStandAloneWeekdayNames) + "," + - "fullTimeFormat=" + fullTimeFormat + "," + - "longTimeFormat=" + longTimeFormat + "," + - "mediumTimeFormat=" + mediumTimeFormat + "," + - "shortTimeFormat=" + shortTimeFormat + "," + - "fullDateFormat=" + fullDateFormat + "," + - "longDateFormat=" + longDateFormat + "," + - "mediumDateFormat=" + mediumDateFormat + "," + - "shortDateFormat=" + shortDateFormat + "," + - "zeroDigit=" + zeroDigit + "," + - "decimalSeparator=" + decimalSeparator + "," + - "groupingSeparator=" + groupingSeparator + "," + - "patternSeparator=" + patternSeparator + "," + - "percent=" + percent + "," + - "perMill=" + perMill + "," + - "monetarySeparator=" + monetarySeparator + "," + - "minusSign=" + minusSign + "," + - "exponentSeparator=" + exponentSeparator + "," + - "infinity=" + infinity + "," + - "NaN=" + NaN + "," + - "currencySymbol=" + currencySymbol + "," + - "internationalCurrencySymbol=" + internationalCurrencySymbol + "," + - "numberPattern=" + numberPattern + "," + - "integerPattern=" + integerPattern + "," + - "currencyPattern=" + currencyPattern + "," + - "percentPattern=" + percentPattern + "]"; + return Objects.toString(this); } public String getDateFormat(int style) { diff --git a/luni/src/main/java/libcore/icu/NativeConverter.java b/luni/src/main/java/libcore/icu/NativeConverter.java index 2d8630c..e18f483 100644 --- a/luni/src/main/java/libcore/icu/NativeConverter.java +++ b/luni/src/main/java/libcore/icu/NativeConverter.java @@ -54,19 +54,19 @@ public final class NativeConverter { } } - public static int setCallbackDecode(long converterHandle, CharsetDecoder decoder) { - return setCallbackDecode(converterHandle, - translateCodingErrorAction(decoder.malformedInputAction()), - translateCodingErrorAction(decoder.unmappableCharacterAction()), - decoder.replacement()); + public static void setCallbackDecode(long converterHandle, CharsetDecoder decoder) { + setCallbackDecode(converterHandle, + translateCodingErrorAction(decoder.malformedInputAction()), + translateCodingErrorAction(decoder.unmappableCharacterAction()), + decoder.replacement()); } - private static native int setCallbackDecode(long converterHandle, int onMalformedInput, int onUnmappableInput, String subChars); + private static native void setCallbackDecode(long converterHandle, int onMalformedInput, int onUnmappableInput, String subChars); - public static int setCallbackEncode(long converterHandle, CharsetEncoder encoder) { - return setCallbackEncode(converterHandle, - translateCodingErrorAction(encoder.malformedInputAction()), - translateCodingErrorAction(encoder.unmappableCharacterAction()), - encoder.replacement()); + public static void setCallbackEncode(long converterHandle, CharsetEncoder encoder) { + setCallbackEncode(converterHandle, + translateCodingErrorAction(encoder.malformedInputAction()), + translateCodingErrorAction(encoder.unmappableCharacterAction()), + encoder.replacement()); } - private static native int setCallbackEncode(long converterHandle, int onMalformedInput, int onUnmappableInput, byte[] subBytes); + private static native void setCallbackEncode(long converterHandle, int onMalformedInput, int onUnmappableInput, byte[] subBytes); } diff --git a/luni/src/main/java/libcore/icu/NativeIDN.java b/luni/src/main/java/libcore/icu/NativeIDN.java index 9bf5cb1..db93379 100644 --- a/luni/src/main/java/libcore/icu/NativeIDN.java +++ b/luni/src/main/java/libcore/icu/NativeIDN.java @@ -34,7 +34,7 @@ public final class NativeIDN { private static String convert(String s, int flags, boolean toAscii) { if (s == null) { - throw new NullPointerException(); + throw new NullPointerException("s == null"); } return convertImpl(s, flags, toAscii); } diff --git a/luni/src/main/java/libcore/icu/RuleBasedCollatorICU.java b/luni/src/main/java/libcore/icu/RuleBasedCollatorICU.java index d036c98..4221fe6 100644 --- a/luni/src/main/java/libcore/icu/RuleBasedCollatorICU.java +++ b/luni/src/main/java/libcore/icu/RuleBasedCollatorICU.java @@ -46,7 +46,7 @@ public final class RuleBasedCollatorICU implements Cloneable { public RuleBasedCollatorICU(String rules) throws ParseException { if (rules == null) { - throw new NullPointerException(); + throw new NullPointerException("rules == null"); } address = NativeCollation.openCollatorFromRules(rules, VALUE_OFF, VALUE_DEFAULT_STRENGTH); } diff --git a/luni/src/main/java/libcore/io/BlockGuardOs.java b/luni/src/main/java/libcore/io/BlockGuardOs.java index 4f2858d..c61a3cf 100644 --- a/luni/src/main/java/libcore/io/BlockGuardOs.java +++ b/luni/src/main/java/libcore/io/BlockGuardOs.java @@ -50,7 +50,7 @@ public class BlockGuardOs extends ForwardingOs { } } - @Override public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException { + @Override public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException { BlockGuard.getThreadPolicy().onNetwork(); return tagSocket(os.accept(fd, peerAddress)); } @@ -80,7 +80,7 @@ public class BlockGuardOs extends ForwardingOs { return linger.isOn() && linger.l_linger > 0; } - @Override public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException { + @Override public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException { BlockGuard.getThreadPolicy().onNetwork(); os.connect(fd, address, port); } @@ -154,22 +154,22 @@ public class BlockGuardOs extends ForwardingOs { return os.readv(fd, buffers, offsets, byteCounts); } - @Override public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException { + @Override public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { BlockGuard.getThreadPolicy().onNetwork(); return os.recvfrom(fd, buffer, flags, srcAddress); } - @Override public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException { + @Override public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { BlockGuard.getThreadPolicy().onNetwork(); return os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress); } - @Override public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException { + @Override public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { BlockGuard.getThreadPolicy().onNetwork(); return os.sendto(fd, buffer, flags, inetAddress, port); } - @Override public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException { + @Override public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { // We permit datagrams without hostname lookups. if (inetAddress != null) { BlockGuard.getThreadPolicy().onNetwork(); @@ -181,6 +181,12 @@ public class BlockGuardOs extends ForwardingOs { return tagSocket(os.socket(domain, type, protocol)); } + @Override public void socketpair(int domain, int type, int protocol, FileDescriptor fd1, FileDescriptor fd2) throws ErrnoException { + os.socketpair(domain, type, protocol, fd1, fd2); + tagSocket(fd1); + tagSocket(fd2); + } + @Override public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException { BlockGuard.getThreadPolicy().onWriteToDisk(); return os.write(fd, buffer); diff --git a/luni/src/main/java/libcore/io/DiskLruCache.java b/luni/src/main/java/libcore/io/DiskLruCache.java index fa26624..8338983 100644 --- a/luni/src/main/java/libcore/io/DiskLruCache.java +++ b/luni/src/main/java/libcore/io/DiskLruCache.java @@ -458,9 +458,14 @@ public final class DiskLruCache implements Closeable { // if this edit is creating the entry for the first time, every index must have a value if (success && !entry.readable) { for (int i = 0; i < valueCount; i++) { + if (!editor.written[i]) { + editor.abort(); + throw new IllegalStateException("Newly created entry didn't create value for index " + i); + } if (!entry.getDirtyFile(i).exists()) { editor.abort(); - throw new IllegalStateException("edit didn't create file " + i); + System.logW("DiskLruCache: Newly created entry doesn't have file for index " + i); + return; } } } @@ -659,10 +664,12 @@ public final class DiskLruCache implements Closeable { */ public final class Editor { private final Entry entry; + private final boolean[] written; private boolean hasErrors; private Editor(Entry entry) { this.entry = entry; + this.written = (entry.readable) ? null : new boolean[valueCount]; } /** @@ -702,6 +709,9 @@ public final class DiskLruCache implements Closeable { if (entry.currentEditor != this) { throw new IllegalStateException(); } + if (!entry.readable) { + written[index] = true; + } return new FaultHidingOutputStream(new FileOutputStream(entry.getDirtyFile(index))); } } diff --git a/luni/src/main/java/libcore/io/DropBox.java b/luni/src/main/java/libcore/io/DropBox.java new file mode 100644 index 0000000..cf88106 --- /dev/null +++ b/luni/src/main/java/libcore/io/DropBox.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package libcore.io; + +public final class DropBox { + + /** + * Hook for customizing how events are reported. + */ + private static volatile Reporter REPORTER = new DefaultReporter(); + + /** + * Used to replace default Reporter for logging events. Must be non-null. + */ + public static void setReporter(Reporter reporter) { + if (reporter == null) { + throw new NullPointerException("reporter == null"); + } + REPORTER = reporter; + } + + /** + * Returns non-null Reporter. + */ + public static Reporter getReporter() { + return REPORTER; + } + + /** + * Interface to allow customization of reporting behavior. + */ + public static interface Reporter { + public void addData(String tag, byte[] data, int flags); + public void addText(String tag, String data); + } + + /** + * Default Reporter which reports events to the log. + */ + private static final class DefaultReporter implements Reporter { + + public void addData(String tag, byte[] data, int flags) { + System.out.println(tag + ": " + Base64.encode(data)); + } + + public void addText(String tag, String data) { + System.out.println(tag + ": " + data); + } + } + + public static void addData(String tag, byte[] data, int flags) { + getReporter().addData(tag, data, flags); + } + + public static void addText(String tag, String data) { + getReporter().addText(tag, data); + } +} diff --git a/luni/src/main/java/libcore/io/EventLogger.java b/luni/src/main/java/libcore/io/EventLogger.java new file mode 100644 index 0000000..9709cc9 --- /dev/null +++ b/luni/src/main/java/libcore/io/EventLogger.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package libcore.io; + +public final class EventLogger { + + /** + * Hook for customizing how events are reported. + */ + private static volatile Reporter REPORTER = new DefaultReporter(); + + /** + * Used to replace default Reporter for logging events. Must be non-null. + */ + public static void setReporter(Reporter reporter) { + if (reporter == null) { + throw new NullPointerException("reporter == null"); + } + REPORTER = reporter; + } + + /** + * Returns non-null Reporter. + */ + public static Reporter getReporter() { + return REPORTER; + } + + /** + * Interface to allow customization of reporting behavior. + */ + public static interface Reporter { + public void report (int code, Object... list); + } + + /** + * Default Reporter which reports events to the log. + */ + private static final class DefaultReporter implements Reporter { + @Override + public void report (int code, Object... list) { + StringBuilder sb = new StringBuilder(); + sb.append(code); + for (Object o : list) { + sb.append(","); + sb.append(o.toString()); + } + System.out.println(sb); + } + } + + public static void writeEvent(int code, Object... list) { + getReporter().report(code, list); + } +} diff --git a/luni/src/main/java/libcore/io/ForwardingOs.java b/luni/src/main/java/libcore/io/ForwardingOs.java index 2c8e562..ee26d04 100644 --- a/luni/src/main/java/libcore/io/ForwardingOs.java +++ b/luni/src/main/java/libcore/io/ForwardingOs.java @@ -20,6 +20,7 @@ import java.io.FileDescriptor; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; +import java.net.SocketException; import java.nio.ByteBuffer; import libcore.util.MutableInt; import libcore.util.MutableLong; @@ -34,15 +35,18 @@ public class ForwardingOs implements Os { this.os = os; } - public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException { return os.accept(fd, peerAddress); } + public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException { return os.accept(fd, peerAddress); } public boolean access(String path, int mode) throws ErrnoException { return os.access(path, mode); } - public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException { os.bind(fd, address, port); } + public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException { os.bind(fd, address, port); } public void chmod(String path, int mode) throws ErrnoException { os.chmod(path, mode); } + public void chown(String path, int uid, int gid) throws ErrnoException { os.chown(path, uid, gid); } public void close(FileDescriptor fd) throws ErrnoException { os.close(fd); } - public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException { os.connect(fd, address, port); } + public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException { os.connect(fd, address, port); } public FileDescriptor dup(FileDescriptor oldFd) throws ErrnoException { return os.dup(oldFd); } public FileDescriptor dup2(FileDescriptor oldFd, int newFd) throws ErrnoException { return os.dup2(oldFd, newFd); } public String[] environ() { return os.environ(); } + public void fchmod(FileDescriptor fd, int mode) throws ErrnoException { os.fchmod(fd, mode); } + public void fchown(FileDescriptor fd, int uid, int gid) throws ErrnoException { os.fchown(fd, uid, gid); } public int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException { return os.fcntlVoid(fd, cmd); } public int fcntlLong(FileDescriptor fd, int cmd, long arg) throws ErrnoException { return os.fcntlLong(fd, cmd, arg); } public int fcntlFlock(FileDescriptor fd, int cmd, StructFlock arg) throws ErrnoException { return os.fcntlFlock(fd, cmd, arg); } @@ -75,6 +79,7 @@ public class ForwardingOs implements Os { public int ioctlInt(FileDescriptor fd, int cmd, MutableInt arg) throws ErrnoException { return os.ioctlInt(fd, cmd, arg); } public boolean isatty(FileDescriptor fd) { return os.isatty(fd); } public void kill(int pid, int signal) throws ErrnoException { os.kill(pid, signal); } + public void lchown(String path, int uid, int gid) throws ErrnoException { os.lchown(path, uid, gid); } public void listen(FileDescriptor fd, int backlog) throws ErrnoException { os.listen(fd, backlog); } public long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException { return os.lseek(fd, offset, whence); } public StructStat lstat(String path) throws ErrnoException { return os.lstat(path); } @@ -95,16 +100,17 @@ public class ForwardingOs implements Os { public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException { return os.read(fd, buffer); } public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException { return os.read(fd, bytes, byteOffset, byteCount); } public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException { return os.readv(fd, buffers, offsets, byteCounts); } - public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException { return os.recvfrom(fd, buffer, flags, srcAddress); } - public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException { return os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress); } + public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { return os.recvfrom(fd, buffer, flags, srcAddress); } + public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { return os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress); } public void remove(String path) throws ErrnoException { os.remove(path); } public void rename(String oldPath, String newPath) throws ErrnoException { os.rename(oldPath, newPath); } public long sendfile(FileDescriptor outFd, FileDescriptor inFd, MutableLong inOffset, long byteCount) throws ErrnoException { return os.sendfile(outFd, inFd, inOffset, byteCount); } - public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException { return os.sendto(fd, buffer, flags, inetAddress, port); } - public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException { return os.sendto(fd, bytes, byteOffset, byteCount, flags, inetAddress, port); } + public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { return os.sendto(fd, buffer, flags, inetAddress, port); } + public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { return os.sendto(fd, bytes, byteOffset, byteCount, flags, inetAddress, port); } public void setegid(int egid) throws ErrnoException { os.setegid(egid); } public void seteuid(int euid) throws ErrnoException { os.seteuid(euid); } public void setgid(int gid) throws ErrnoException { os.setgid(gid); } + public int setsid() throws ErrnoException { return os.setsid(); } public void setsockoptByte(FileDescriptor fd, int level, int option, int value) throws ErrnoException { os.setsockoptByte(fd, level, option, value); } public void setsockoptIfreq(FileDescriptor fd, int level, int option, String value) throws ErrnoException { os.setsockoptIfreq(fd, level, option, value); } public void setsockoptInt(FileDescriptor fd, int level, int option, int value) throws ErrnoException { os.setsockoptInt(fd, level, option, value); } @@ -115,11 +121,14 @@ public class ForwardingOs implements Os { public void setuid(int uid) throws ErrnoException { os.setuid(uid); } public void shutdown(FileDescriptor fd, int how) throws ErrnoException { os.shutdown(fd, how); } public FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException { return os.socket(domain, type, protocol); } + public void socketpair(int domain, int type, int protocol, FileDescriptor fd1, FileDescriptor fd2) throws ErrnoException { os.socketpair(domain, type, protocol, fd1, fd2); } public StructStat stat(String path) throws ErrnoException { return os.stat(path); } public StructStatFs statfs(String path) throws ErrnoException { return os.statfs(path); } public String strerror(int errno) { return os.strerror(errno); } public void symlink(String oldPath, String newPath) throws ErrnoException { os.symlink(oldPath, newPath); } public long sysconf(int name) { return os.sysconf(name); } + public void tcdrain(FileDescriptor fd) throws ErrnoException { os.tcdrain(fd); } + public int umask(int mask) { return os.umask(mask); } public StructUtsname uname() { return os.uname(); } public int waitpid(int pid, MutableInt status, int options) throws ErrnoException { return os.waitpid(pid, status, options); } public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException { return os.write(fd, buffer); } diff --git a/luni/src/main/java/libcore/io/Os.java b/luni/src/main/java/libcore/io/Os.java index d637b67..1f42497 100644 --- a/luni/src/main/java/libcore/io/Os.java +++ b/luni/src/main/java/libcore/io/Os.java @@ -20,20 +20,24 @@ import java.io.FileDescriptor; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; +import java.net.SocketException; import java.nio.ByteBuffer; import libcore.util.MutableInt; import libcore.util.MutableLong; public interface Os { - public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException; + public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException; public boolean access(String path, int mode) throws ErrnoException; - public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException; + public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException; public void chmod(String path, int mode) throws ErrnoException; + public void chown(String path, int uid, int gid) throws ErrnoException; public void close(FileDescriptor fd) throws ErrnoException; - public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException; + public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException; public FileDescriptor dup(FileDescriptor oldFd) throws ErrnoException; public FileDescriptor dup2(FileDescriptor oldFd, int newFd) throws ErrnoException; public String[] environ(); + public void fchmod(FileDescriptor fd, int mode) throws ErrnoException; + public void fchown(FileDescriptor fd, int uid, int gid) throws ErrnoException; public int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException; public int fcntlLong(FileDescriptor fd, int cmd, long arg) throws ErrnoException; public int fcntlFlock(FileDescriptor fd, int cmd, StructFlock arg) throws ErrnoException; @@ -67,6 +71,7 @@ public interface Os { public int ioctlInt(FileDescriptor fd, int cmd, MutableInt arg) throws ErrnoException; public boolean isatty(FileDescriptor fd); public void kill(int pid, int signal) throws ErrnoException; + public void lchown(String path, int uid, int gid) throws ErrnoException; public void listen(FileDescriptor fd, int backlog) throws ErrnoException; public long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException; public StructStat lstat(String path) throws ErrnoException; @@ -88,16 +93,17 @@ public interface Os { public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException; public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException; public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException; - public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException; - public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException; + public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException; + public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException; public void remove(String path) throws ErrnoException; public void rename(String oldPath, String newPath) throws ErrnoException; - public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException; - public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException; + public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException; + public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException; public long sendfile(FileDescriptor outFd, FileDescriptor inFd, MutableLong inOffset, long byteCount) throws ErrnoException; public void setegid(int egid) throws ErrnoException; public void seteuid(int euid) throws ErrnoException; public void setgid(int gid) throws ErrnoException; + public int setsid() throws ErrnoException; public void setsockoptByte(FileDescriptor fd, int level, int option, int value) throws ErrnoException; public void setsockoptIfreq(FileDescriptor fd, int level, int option, String value) throws ErrnoException; public void setsockoptInt(FileDescriptor fd, int level, int option, int value) throws ErrnoException; @@ -108,12 +114,15 @@ public interface Os { public void setuid(int uid) throws ErrnoException; public void shutdown(FileDescriptor fd, int how) throws ErrnoException; public FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException; + public void socketpair(int domain, int type, int protocol, FileDescriptor fd1, FileDescriptor fd2) throws ErrnoException; public StructStat stat(String path) throws ErrnoException; /* TODO: replace statfs with statvfs. */ public StructStatFs statfs(String path) throws ErrnoException; public String strerror(int errno); public void symlink(String oldPath, String newPath) throws ErrnoException; public long sysconf(int name); + public void tcdrain(FileDescriptor fd) throws ErrnoException; + public int umask(int mask); public StructUtsname uname(); public int waitpid(int pid, MutableInt status, int options) throws ErrnoException; public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException; diff --git a/luni/src/main/java/libcore/io/OsConstants.java b/luni/src/main/java/libcore/io/OsConstants.java index 68a165c..9f381f5 100644 --- a/luni/src/main/java/libcore/io/OsConstants.java +++ b/luni/src/main/java/libcore/io/OsConstants.java @@ -224,6 +224,7 @@ public final class OsConstants { public static final int O_CREAT = placeholder(); public static final int O_EXCL = placeholder(); public static final int O_NOCTTY = placeholder(); + public static final int O_NOFOLLOW = placeholder(); public static final int O_NONBLOCK = placeholder(); public static final int O_RDONLY = placeholder(); public static final int O_RDWR = placeholder(); diff --git a/luni/src/main/java/libcore/io/Posix.java b/luni/src/main/java/libcore/io/Posix.java index 7bbf49f..2fb187e 100644 --- a/luni/src/main/java/libcore/io/Posix.java +++ b/luni/src/main/java/libcore/io/Posix.java @@ -20,6 +20,7 @@ import java.io.FileDescriptor; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; +import java.net.SocketException; import java.nio.ByteBuffer; import java.nio.NioUtils; import libcore.util.MutableInt; @@ -28,15 +29,18 @@ import libcore.util.MutableLong; public final class Posix implements Os { Posix() { } - public native FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException; + public native FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException; public native boolean access(String path, int mode) throws ErrnoException; - public native void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException; + public native void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException; public native void chmod(String path, int mode) throws ErrnoException; + public native void chown(String path, int uid, int gid) throws ErrnoException; public native void close(FileDescriptor fd) throws ErrnoException; - public native void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException; + public native void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException; public native FileDescriptor dup(FileDescriptor oldFd) throws ErrnoException; public native FileDescriptor dup2(FileDescriptor oldFd, int newFd) throws ErrnoException; public native String[] environ(); + public native void fchmod(FileDescriptor fd, int mode) throws ErrnoException; + public native void fchown(FileDescriptor fd, int uid, int gid) throws ErrnoException; public native int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException; public native int fcntlLong(FileDescriptor fd, int cmd, long arg) throws ErrnoException; public native int fcntlFlock(FileDescriptor fd, int cmd, StructFlock arg) throws ErrnoException; @@ -69,6 +73,7 @@ public final class Posix implements Os { public native int ioctlInt(FileDescriptor fd, int cmd, MutableInt arg) throws ErrnoException; public native boolean isatty(FileDescriptor fd); public native void kill(int pid, int signal) throws ErrnoException; + public native void lchown(String path, int uid, int gid) throws ErrnoException; public native void listen(FileDescriptor fd, int backlog) throws ErrnoException; public native long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException; public native StructStat lstat(String path) throws ErrnoException; @@ -119,36 +124,37 @@ public final class Posix implements Os { } private native int readBytes(FileDescriptor fd, Object buffer, int offset, int byteCount) throws ErrnoException; public native int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException; - public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException { + public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { if (buffer.isDirect()) { return recvfromBytes(fd, buffer, buffer.position(), buffer.remaining(), flags, srcAddress); } else { return recvfromBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + buffer.position(), buffer.remaining(), flags, srcAddress); } } - public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException { + public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { // This indirection isn't strictly necessary, but ensures that our public interface is type safe. return recvfromBytes(fd, bytes, byteOffset, byteCount, flags, srcAddress); } - private native int recvfromBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException; + private native int recvfromBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException; public native void remove(String path) throws ErrnoException; public native void rename(String oldPath, String newPath) throws ErrnoException; public native long sendfile(FileDescriptor outFd, FileDescriptor inFd, MutableLong inOffset, long byteCount) throws ErrnoException; - public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException { + public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { if (buffer.isDirect()) { return sendtoBytes(fd, buffer, buffer.position(), buffer.remaining(), flags, inetAddress, port); } else { return sendtoBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + buffer.position(), buffer.remaining(), flags, inetAddress, port); } } - public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException { + public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { // This indirection isn't strictly necessary, but ensures that our public interface is type safe. return sendtoBytes(fd, bytes, byteOffset, byteCount, flags, inetAddress, port); } - private native int sendtoBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException; + private native int sendtoBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException; public native void setegid(int egid) throws ErrnoException; public native void seteuid(int euid) throws ErrnoException; public native void setgid(int gid) throws ErrnoException; + public native int setsid() throws ErrnoException; public native void setsockoptByte(FileDescriptor fd, int level, int option, int value) throws ErrnoException; public native void setsockoptIfreq(FileDescriptor fd, int level, int option, String value) throws ErrnoException; public native void setsockoptInt(FileDescriptor fd, int level, int option, int value) throws ErrnoException; @@ -159,11 +165,14 @@ public final class Posix implements Os { public native void setuid(int uid) throws ErrnoException; public native void shutdown(FileDescriptor fd, int how) throws ErrnoException; public native FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException; + public native void socketpair(int domain, int type, int protocol, FileDescriptor fd1, FileDescriptor fd2) throws ErrnoException; public native StructStat stat(String path) throws ErrnoException; public native StructStatFs statfs(String path) throws ErrnoException; public native String strerror(int errno); public native void symlink(String oldPath, String newPath) throws ErrnoException; public native long sysconf(int name); + public native void tcdrain(FileDescriptor fd) throws ErrnoException; + public native int umask(int mask); public native StructUtsname uname(); public native int waitpid(int pid, MutableInt status, int options) throws ErrnoException; public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException { diff --git a/luni/src/main/java/libcore/io/StrictLineReader.java b/luni/src/main/java/libcore/io/StrictLineReader.java index 5f8d452..36556a0 100644 --- a/luni/src/main/java/libcore/io/StrictLineReader.java +++ b/luni/src/main/java/libcore/io/StrictLineReader.java @@ -106,8 +106,10 @@ public class StrictLineReader implements Closeable { * or the specified charset is not supported. */ public StrictLineReader(InputStream in, int capacity, Charset charset) { - if (in == null || charset == null) { - throw new NullPointerException(); + if (in == null) { + throw new NullPointerException("in == null"); + } else if (charset == null) { + throw new NullPointerException("charset == null"); } if (capacity < 0) { throw new IllegalArgumentException("capacity <= 0"); diff --git a/luni/src/main/java/libcore/net/MimeUtils.java b/luni/src/main/java/libcore/net/MimeUtils.java index f8038f0..6ea0baf 100644 --- a/luni/src/main/java/libcore/net/MimeUtils.java +++ b/luni/src/main/java/libcore/net/MimeUtils.java @@ -350,6 +350,7 @@ public final class MimeUtils { add("video/x-ms-wvx", "wvx"); add("video/x-msvideo", "avi"); add("video/x-sgi-movie", "movie"); + add("video/x-webex", "wrf"); add("x-conference/x-cooltalk", "ice"); add("x-epoc/x-sisx-app", "sisx"); applyOverrides(); diff --git a/luni/src/main/java/libcore/net/UriCodec.java b/luni/src/main/java/libcore/net/UriCodec.java index bde922b..6624474 100644 --- a/luni/src/main/java/libcore/net/UriCodec.java +++ b/luni/src/main/java/libcore/net/UriCodec.java @@ -94,7 +94,7 @@ public abstract class UriCodec { private void appendEncoded(StringBuilder builder, String s, Charset charset, boolean isPartiallyEncoded) { if (s == null) { - throw new NullPointerException(); + throw new NullPointerException("s == null"); } int escapeStart = -1; diff --git a/luni/src/main/java/libcore/net/http/HttpConnection.java b/luni/src/main/java/libcore/net/http/HttpConnection.java index 2f92706..4a6e65d 100644 --- a/luni/src/main/java/libcore/net/http/HttpConnection.java +++ b/luni/src/main/java/libcore/net/http/HttpConnection.java @@ -199,7 +199,6 @@ final class HttpConnection { // tlsTolerant mimics Chrome's behavior if (tlsTolerant && unverifiedSocket instanceof OpenSSLSocketImpl) { OpenSSLSocketImpl openSslSocket = (OpenSSLSocketImpl) unverifiedSocket; - openSslSocket.setEnabledCompressionMethods(new String[] { "ZLIB"}); openSslSocket.setUseSessionTickets(true); openSslSocket.setHostname(address.uriHost); // use SSLSocketFactory default enabled protocols diff --git a/luni/src/main/java/libcore/net/http/HttpEngine.java b/luni/src/main/java/libcore/net/http/HttpEngine.java index 3e4b9d3..42c9b96 100644 --- a/luni/src/main/java/libcore/net/http/HttpEngine.java +++ b/luni/src/main/java/libcore/net/http/HttpEngine.java @@ -69,10 +69,10 @@ import libcore.util.EmptyArray; * required, use {@link #automaticallyReleaseConnectionToPool()}. */ public class HttpEngine { - private static final CacheResponse BAD_GATEWAY_RESPONSE = new CacheResponse() { + private static final CacheResponse GATEWAY_TIMEOUT_RESPONSE = new CacheResponse() { @Override public Map<String, List<String>> getHeaders() throws IOException { Map<String, List<String>> result = new HashMap<String, List<String>>(); - result.put(null, Collections.singletonList("HTTP/1.1 502 Bad Gateway")); + result.put(null, Collections.singletonList("HTTP/1.1 504 Gateway Timeout")); return result; } @Override public InputStream getBody() throws IOException { @@ -223,14 +223,15 @@ public class HttpEngine { /* * The raw response source may require the network, but the request * headers may forbid network use. In that case, dispose of the network - * response and use a BAD_GATEWAY response instead. + * response and use a GATEWAY_TIMEOUT response instead, as specified + * by http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.4. */ if (requestHeaders.isOnlyIfCached() && responseSource.requiresConnection()) { if (responseSource == ResponseSource.CONDITIONAL_CACHE) { IoUtils.closeQuietly(cachedResponseBody); } this.responseSource = ResponseSource.CACHE; - this.cacheResponse = BAD_GATEWAY_RESPONSE; + this.cacheResponse = GATEWAY_TIMEOUT_RESPONSE; RawHeaders rawResponseHeaders = RawHeaders.fromMultimap(cacheResponse.getHeaders()); setResponse(new ResponseHeaders(uri, rawResponseHeaders), cacheResponse.getBody()); } @@ -490,8 +491,15 @@ public class HttpEngine { reusable = false; } - // If the headers specify that the connection shouldn't be reused, don't reuse it. - if (hasConnectionCloseHeader()) { + // If the request specified that the connection shouldn't be reused, + // don't reuse it. This advice doesn't apply to CONNECT requests because + // the "Connection: close" header goes the origin server, not the proxy. + if (requestHeaders.hasConnectionClose() && method != CONNECT) { + reusable = false; + } + + // If the response specified that the connection shouldn't be reused, don't reuse it. + if (responseHeaders != null && responseHeaders.hasConnectionClose()) { reusable = false; } @@ -523,8 +531,13 @@ public class HttpEngine { /* * If the response was transparently gzipped, remove the gzip header field * so clients don't double decompress. http://b/3009828 + * + * Also remove the Content-Length in this case because it contains the length + * of the gzipped response. This isn't terribly useful and is dangerous because + * clients can query the content length, but not the content encoding. */ responseHeaders.stripContentEncoding(); + responseHeaders.stripContentLength(); responseBodyIn = new GZIPInputStream(transferStream); } else { responseBodyIn = transferStream; @@ -758,11 +771,6 @@ public class HttpEngine { return agent != null ? agent : ("Java" + System.getProperty("java.version")); } - private boolean hasConnectionCloseHeader() { - return (responseHeaders != null && responseHeaders.hasConnectionClose()) - || requestHeaders.hasConnectionClose(); - } - protected final String getOriginAddress(URL url) { int port = url.getPort(); String result = url.getHost(); diff --git a/luni/src/main/java/libcore/net/http/HttpResponseCache.java b/luni/src/main/java/libcore/net/http/HttpResponseCache.java index db036b6..1a9dfd1 100644 --- a/luni/src/main/java/libcore/net/http/HttpResponseCache.java +++ b/luni/src/main/java/libcore/net/http/HttpResponseCache.java @@ -426,7 +426,7 @@ public final class HttpResponseCache extends ResponseCache implements ExtendedRe } public void writeTo(DiskLruCache.Editor editor) throws IOException { - OutputStream out = editor.newOutputStream(0); + OutputStream out = editor.newOutputStream(ENTRY_METADATA); Writer writer = new BufferedWriter(new OutputStreamWriter(out, Charsets.UTF_8)); writer.write(uri + '\n'); diff --git a/luni/src/main/java/libcore/net/http/HttpURLConnectionImpl.java b/luni/src/main/java/libcore/net/http/HttpURLConnectionImpl.java index a59df55..260a9ad 100644 --- a/luni/src/main/java/libcore/net/http/HttpURLConnectionImpl.java +++ b/luni/src/main/java/libcore/net/http/HttpURLConnectionImpl.java @@ -36,6 +36,7 @@ import java.security.Permission; import java.util.List; import java.util.Map; import libcore.io.Base64; +import libcore.io.IoUtils; /** * This implementation uses HttpEngine to send requests and receive responses. @@ -87,6 +88,14 @@ class HttpURLConnectionImpl extends HttpURLConnection { @Override public final void disconnect() { // Calling disconnect() before a connection exists should have no effect. if (httpEngine != null) { + // We close the response body here instead of in + // HttpEngine.release because that is called when input + // has been completely read from the underlying socket. + // However the response body can be a GZIPInputStream that + // still has unread data. + if (httpEngine.hasResponse()) { + IoUtils.closeQuietly(httpEngine.getResponseBody()); + } httpEngine.release(false); } } diff --git a/luni/src/main/java/libcore/net/http/ResponseHeaders.java b/luni/src/main/java/libcore/net/http/ResponseHeaders.java index 003b445..c0b4200 100644 --- a/luni/src/main/java/libcore/net/http/ResponseHeaders.java +++ b/luni/src/main/java/libcore/net/http/ResponseHeaders.java @@ -187,6 +187,11 @@ public final class ResponseHeaders { headers.removeAll("Content-Encoding"); } + public void stripContentLength() { + contentLength = -1; + headers.removeAll("Content-Length"); + } + public boolean isChunked() { return "chunked".equalsIgnoreCase(transferEncoding); } diff --git a/luni/src/main/java/libcore/util/BasicLruCache.java b/luni/src/main/java/libcore/util/BasicLruCache.java index 13ab3db..75e4a75 100644 --- a/luni/src/main/java/libcore/util/BasicLruCache.java +++ b/luni/src/main/java/libcore/util/BasicLruCache.java @@ -43,7 +43,7 @@ public class BasicLruCache<K, V> { */ public synchronized final V get(K key) { if (key == null) { - throw new NullPointerException(); + throw new NullPointerException("key == null"); } V result = map.get(key); @@ -68,8 +68,10 @@ public class BasicLruCache<K, V> { * no longer cached, it has not been passed to {@link #entryEvicted}. */ public synchronized final V put(K key, V value) { - if (key == null || value == null) { - throw new NullPointerException(); + if (key == null) { + throw new NullPointerException("key == null"); + } else if (value == null) { + throw new NullPointerException("value == null"); } V previous = map.put(key, value); diff --git a/luni/src/main/java/libcore/util/Objects.java b/luni/src/main/java/libcore/util/Objects.java index 7817316..573b973 100644 --- a/luni/src/main/java/libcore/util/Objects.java +++ b/luni/src/main/java/libcore/util/Objects.java @@ -16,6 +16,10 @@ package libcore.util; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Arrays; + public final class Objects { private Objects() {} @@ -29,4 +33,64 @@ public final class Objects { public static int hashCode(Object o) { return (o == null) ? 0 : o.hashCode(); } + + /** + * Returns a string reporting the value of each declared field, via reflection. + * Static and transient fields are automatically skipped. Produces output like + * "SimpleClassName[integer=1234,string="hello",character='c',intArray=[1,2,3]]". + */ + public static String toString(Object o) { + Class<?> c = o.getClass(); + StringBuilder sb = new StringBuilder(); + sb.append(c.getSimpleName()).append('['); + int i = 0; + for (Field f : c.getDeclaredFields()) { + if ((f.getModifiers() & (Modifier.STATIC | Modifier.TRANSIENT)) != 0) { + continue; + } + f.setAccessible(true); + try { + Object value = f.get(o); + + if (i++ > 0) { + sb.append(','); + } + + sb.append(f.getName()); + sb.append('='); + + if (value.getClass().isArray()) { + if (value.getClass() == boolean[].class) { + sb.append(Arrays.toString((boolean[]) value)); + } else if (value.getClass() == byte[].class) { + sb.append(Arrays.toString((byte[]) value)); + } else if (value.getClass() == char[].class) { + sb.append(Arrays.toString((char[]) value)); + } else if (value.getClass() == double[].class) { + sb.append(Arrays.toString((double[]) value)); + } else if (value.getClass() == float[].class) { + sb.append(Arrays.toString((float[]) value)); + } else if (value.getClass() == int[].class) { + sb.append(Arrays.toString((int[]) value)); + } else if (value.getClass() == long[].class) { + sb.append(Arrays.toString((long[]) value)); + } else if (value.getClass() == short[].class) { + sb.append(Arrays.toString((short[]) value)); + } else { + sb.append(Arrays.toString((Object[]) value)); + } + } else if (value.getClass() == Character.class) { + sb.append('\'').append(value).append('\''); + } else if (value.getClass() == String.class) { + sb.append('"').append(value).append('"'); + } else { + sb.append(value); + } + } catch (IllegalAccessException unexpected) { + throw new AssertionError(unexpected); + } + } + sb.append("]"); + return sb.toString(); + } } diff --git a/luni/src/main/java/libcore/util/ZoneInfo.java b/luni/src/main/java/libcore/util/ZoneInfo.java index 5a8caf2..ac48b23 100644 --- a/luni/src/main/java/libcore/util/ZoneInfo.java +++ b/luni/src/main/java/libcore/util/ZoneInfo.java @@ -51,29 +51,46 @@ public final class ZoneInfo extends TimeZone { private final byte[] mTypes; private final byte[] mIsDsts; private final boolean mUseDst; + private final int mDstSavings; // Implements TimeZone.getDSTSavings. - ZoneInfo(String name, int[] transitions, byte[] type, int[] gmtOffsets, byte[] isDsts) { + ZoneInfo(String name, int[] transitions, byte[] types, int[] gmtOffsets, byte[] isDsts) { mTransitions = transitions; - mTypes = type; + mTypes = types; mIsDsts = isDsts; setID(name); - // Use the latest non-daylight offset (if any) as the raw offset. - int lastStd; - for (lastStd = mTransitions.length - 1; lastStd >= 0; lastStd--) { - if (mIsDsts[mTypes[lastStd] & 0xff] == 0) { - break; + // Find the latest daylight and standard offsets (if any). + int lastStd = 0; + boolean haveStd = false; + int lastDst = 0; + boolean haveDst = false; + for (int i = mTransitions.length - 1; (!haveStd || !haveDst) && i >= 0; --i) { + int type = mTypes[i] & 0xff; + if (!haveStd && mIsDsts[type] == 0) { + haveStd = true; + lastStd = i; + } + if (!haveDst && mIsDsts[type] != 0) { + haveDst = true; + lastDst = i; } } - if (lastStd < 0) { - lastStd = 0; - } + + // Use the latest non-daylight offset (if any) as the raw offset. if (lastStd >= mTypes.length) { mRawOffset = gmtOffsets[0]; } else { mRawOffset = gmtOffsets[mTypes[lastStd] & 0xff]; } + // Use the latest transition's pair of offsets to compute the DST savings. + // This isn't generally useful, but it's exposed by TimeZone.getDSTSavings. + if (lastDst >= mTypes.length) { + mDstSavings = 0; + } else { + mDstSavings = Math.abs(gmtOffsets[mTypes[lastStd] & 0xff] - gmtOffsets[mTypes[lastDst] & 0xff]) * 1000; + } + // Cache the oldest known raw offset, in case we're asked about times that predate our // transition data. int firstStd = -1; @@ -111,6 +128,7 @@ public final class ZoneInfo extends TimeZone { } mUseDst = usesDst; + // tzdata uses seconds, but Java uses milliseconds. mRawOffset *= 1000; mEarliestRawOffset = earliestRawOffset * 1000; } @@ -185,6 +203,10 @@ public final class ZoneInfo extends TimeZone { mRawOffset = off; } + @Override public int getDSTSavings() { + return mUseDst ? mDstSavings: 0; + } + @Override public boolean useDaylightTime() { return mUseDst; } @@ -235,7 +257,7 @@ public final class ZoneInfo extends TimeZone { StringBuilder sb = new StringBuilder(); // First the basics... sb.append(getClass().getName() + "[" + getID() + ",mRawOffset=" + mRawOffset + - ",mUseDst=" + mUseDst + "]"); + ",mUseDst=" + mUseDst + ",mDstSavings=" + mDstSavings + "]"); // ...followed by a zdump(1)-like description of all our transition data. sb.append("\n"); Formatter f = new Formatter(sb); diff --git a/luni/src/main/java/libcore/util/ZoneInfoDB.java b/luni/src/main/java/libcore/util/ZoneInfoDB.java index 69b6784..cc7cc4f 100644 --- a/luni/src/main/java/libcore/util/ZoneInfoDB.java +++ b/luni/src/main/java/libcore/util/ZoneInfoDB.java @@ -180,7 +180,7 @@ public final class ZoneInfoDB { } } - private static TimeZone makeTimeZone(String id) throws IOException { + public static TimeZone makeTimeZone(String id) throws IOException { // Work out where in the big data file this time zone is. int index = Arrays.binarySearch(ids, id); if (index < 0) { @@ -259,17 +259,6 @@ public final class ZoneInfoDB { } } - public static TimeZone getTimeZone(String id) { - if (id == null) { - return null; - } - try { - return makeTimeZone(id); - } catch (IOException ignored) { - return null; - } - } - public static String getVersion() { return VERSION; } diff --git a/luni/src/main/java/org/apache/harmony/crypto/internal/NullCipherSpi.java b/luni/src/main/java/org/apache/harmony/crypto/internal/NullCipherSpi.java index 270f63b..151b403 100644 --- a/luni/src/main/java/org/apache/harmony/crypto/internal/NullCipherSpi.java +++ b/luni/src/main/java/org/apache/harmony/crypto/internal/NullCipherSpi.java @@ -115,8 +115,10 @@ public class NullCipherSpi extends CipherSpi { @Override public int engineUpdate(ByteBuffer input, ByteBuffer output) throws ShortBufferException { - if (input == null || output == null) { - throw new NullPointerException(); + if (input == null) { + throw new NullPointerException("input == null"); + } else if (output == null) { + throw new NullPointerException("output == null"); } int result = input.limit() - input.position(); try { diff --git a/luni/src/main/java/org/apache/harmony/luni/util/TwoKeyHashMap.java b/luni/src/main/java/org/apache/harmony/luni/util/TwoKeyHashMap.java deleted file mode 100644 index 35e6c62..0000000 --- a/luni/src/main/java/org/apache/harmony/luni/util/TwoKeyHashMap.java +++ /dev/null @@ -1,566 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.harmony.luni.util; - -import java.util.AbstractCollection; -import java.util.AbstractMap; -import java.util.AbstractSet; -import java.util.Arrays; -import java.util.Collection; -import java.util.ConcurrentModificationException; -import java.util.Iterator; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; - -/** - * - * Reductive hash with two keys - * - */ -public class TwoKeyHashMap<E, K, V> extends AbstractMap<String, V> { - - static final float DEFAULT_LOAD_FACTOR = 0.75f; - static final int DEFAULT_INITIAL_SIZE = 16; - - private Set<Map.Entry<String, V>> entrySet; - private Collection<V> values; - private int size; - private int arrSize; - private int modCount; - - private Entry<E, K, V>[] arr; - - private float loadFactor; - int threshold = 0; - - /** - * Constructs an empty HashMap - */ - public TwoKeyHashMap() { - this(DEFAULT_INITIAL_SIZE, DEFAULT_LOAD_FACTOR); - } - - /** - * Constructs an empty HashMap - * - * @param initialCapacity - */ - public TwoKeyHashMap(int initialCapacity) { - this(initialCapacity, DEFAULT_LOAD_FACTOR); - } - - /** - * Constructs an empty HashMap - * - * @param initialCapacity - * @param initialLoadFactor - */ - @SuppressWarnings("unchecked") - public TwoKeyHashMap(int initialCapacity, float initialLoadFactor) { - if (initialCapacity < 0) { - throw new IllegalArgumentException("initialCapacity should be >= 0"); - } - if (initialLoadFactor <= 0) { - throw new IllegalArgumentException( - "initialLoadFactor should be > 0"); - } - loadFactor = initialLoadFactor; - if (initialCapacity == Integer.MAX_VALUE) { - initialCapacity--; - } - arrSize = initialCapacity > 0 ? initialCapacity : 1; - threshold = (int) (arrSize * loadFactor); - arr = new Entry[arrSize + 1]; - } - - /** - * Returns a collection view of the values - */ - public Collection<V> values() { - if (values == null) { - values = new ValuesCollectionImpl(); - } - return values; - } - - /** - * Returns a collection view of the mappings - */ - public Set<Map.Entry<String, V>> entrySet() { - if (entrySet == null) { - entrySet = new EntrySetImpl(); - } - return entrySet; - } - - /** - * Clears the map - */ - public void clear() { - modCount++; - size = 0; - Arrays.fill(arr, 0, arr.length, null); - } - - /** - * Removes the mapping for the keys - * - * @param key1 - * @param key2 - * @return - */ - public V remove(Object key1, Object key2) { - Entry<E, K, V> e = removeEntry(key1, key2); - return (e != null) ? e.value : null; - } - - /** - * Associates the specified value with the specified keys in this map - * - * @param key1 - * @param key2 - * @param value - * @return - */ - public V put(E key1, K key2, V value) { - if (key1 == null && key2 == null) { - int index = arrSize; - if (arr[index] == null) { - arr[index] = createEntry(0, null, null, value, null); - size++; - modCount++; - return null; - } else { - V oldValue = arr[index].value; - arr[index].value = value; - return oldValue; - } - } - - int hash = key1.hashCode() + key2.hashCode(); - int index = (hash & 0x7fffffff) % arrSize; - Entry<E, K, V> e = arr[index]; - - while (e != null) { - if (hash == e.hash && key1.equals(e.getKey1()) - && key2.equals(e.getKey2())) { - V oldValue = e.value; - e.value = value; - return oldValue; - } - e = e.next; - } - - arr[index] = createEntry(hash, key1, key2, value, arr[index]); - size++; - modCount++; - - if (size > threshold) { - rehash(); - } - return null; - } - - /** - * Rehash the map - * - */ - @SuppressWarnings("unchecked") - void rehash() { - int newArrSize = (arrSize + 1) * 2 + 1; - if (newArrSize < 0) { - newArrSize = Integer.MAX_VALUE - 1; - } - Entry<E, K, V>[] newArr = new Entry[newArrSize + 1]; - - for (int i = 0; i < arr.length - 1; i++) { - Entry<E, K, V> entry = arr[i]; - while (entry != null) { - Entry<E, K, V> next = entry.next; - - int newIndex = (entry.hash & 0x7fffffff) % newArrSize; - entry.next = newArr[newIndex]; - newArr[newIndex] = entry; - - entry = next; - } - } - newArr[newArrSize] = arr[arrSize]; // move null entry - arrSize = newArrSize; - - // The maximum array size is reached, increased loadFactor - // will keep array from further growing - if (arrSize == Integer.MAX_VALUE) { - loadFactor *= 10; - } - threshold = (int) (arrSize * loadFactor); - arr = newArr; - } - - /** - * Returns true if this map contains a mapping for {@code key1} and {@code key2}. - */ - public boolean containsKey(Object key1, Object key2) { - return findEntry(key1, key2) != null; - } - - /** - * Return the value by keys - * - * @param key1 - * @param key2 - * @return - */ - public V get(Object key1, Object key2) { - Entry<E, K, V> e = findEntry(key1, key2); - if (e != null) { - return e.value; - } - return null; - } - - /** - * Returns true if this map contains no key-value mappings - */ - public boolean isEmpty() { - return size == 0; - } - - /** - * Returns the number of mappings - */ - public int size() { - return size; - } - - /** - * Creates new entry - * - * @param hashCode - * @param key1 - * @param key2 - * @param value - * @param next - * @return - */ - Entry<E, K, V> createEntry(int hashCode, E key1, K key2, V value, - Entry<E, K, V> next) { - return new Entry<E, K, V>(hashCode, key1, key2, value, next); - } - - /** - * Creates entries iterator - * - * @return - */ - Iterator<Map.Entry<String, V>> createEntrySetIterator() { - return new EntryIteratorImpl(); - } - - /** - * Creates values iterator - * - * @return - */ - Iterator<V> createValueCollectionIterator() { - return new ValueIteratorImpl(); - } - - /** - * Entry implementation for the TwoKeyHashMap class - * - */ - public static class Entry<E, K, V> implements Map.Entry<String, V> { - int hash; - E key1; - K key2; - V value; - Entry<E, K, V> next; - - public Entry(int hash, E key1, K key2, V value, Entry<E, K, V> next) { - this.hash = hash; - this.key1 = key1; - this.key2 = key2; - this.value = value; - this.next = next; - } - - public String getKey() { - return key1.toString() + key2.toString(); - } - - public E getKey1() { - return key1; - } - - public K getKey2() { - return key2; - } - - public V getValue() { - return value; - } - - public V setValue(V value) { - V oldValue = this.value; - this.value = value; - return oldValue; - } - - public boolean equals(Object obj) { - if (!(obj instanceof Entry)) { - return false; - } - - Entry<?, ?, ?> e = (Entry<?, ?, ?>) obj; - Object getKey1 = e.getKey1(); - Object getKey2 = e.getKey2(); - Object getValue = e.getValue(); - if ((key1 == null && getKey1 != null) - || (key2 == null && getKey2 != null) - || (value == null && getValue != null) - || !key1.equals(e.getKey1()) || !key2.equals(e.getKey2()) - || !value.equals(getValue)) { - return false; - } - return true; - } - - public int hashCode() { - int hash1 = (key1 == null ? 0 : key1.hashCode()); - int hash2 = (key2 == null ? 0 : key2.hashCode()); - return (hash1 + hash2) ^ (value == null ? 0 : value.hashCode()); - } - - } - - class EntrySetImpl extends AbstractSet<Map.Entry<String, V>> { - public int size() { - return size; - } - - public void clear() { - TwoKeyHashMap.this.clear(); - } - - public boolean isEmpty() { - return size == 0; - } - - public boolean contains(Object obj) { - if (!(obj instanceof Entry)) { - return false; - } - - Entry<?, ?, ?> entry = (Entry<?, ?, ?>) obj; - Entry<E, K, V> entry2 = findEntry(entry.getKey1(), entry.getKey2()); - if (entry2 == null) { - return false; - } - Object value = entry.getValue(); - Object value2 = entry2.getValue(); - return value == null ? value2 == null : value.equals(value2); - } - - public boolean remove(Object obj) { - if (!(obj instanceof Entry)) { - return false; - } - return removeEntry(((Entry) obj).getKey1(), ((Entry) obj).getKey2()) != null; - } - - public Iterator<Map.Entry<String, V>> iterator() { - return createEntrySetIterator(); - } - } - - // Iterates Entries inside the Map - class EntryIteratorImpl implements Iterator<Map.Entry<String, V>> { - private int startModCount; - private boolean found; - private int curr = -1; - private int returned_index = -1; - private Entry<E, K, V> curr_entry; - private Entry<E, K, V> returned_entry; - - EntryIteratorImpl() { - startModCount = modCount; - } - - public boolean hasNext() { - if (found) { - return true; - } - if (curr_entry != null) { - curr_entry = curr_entry.next; - } - if (curr_entry == null) { - for (curr++; curr < arr.length && arr[curr] == null; curr++) { - } - - if (curr < arr.length) { - curr_entry = arr[curr]; - } - } - return found = (curr_entry != null); - } - - public Map.Entry<String, V> next() { - if (modCount != startModCount) { - throw new ConcurrentModificationException(); - } - if (!hasNext()) { - throw new NoSuchElementException(); - } - - found = false; - returned_index = curr; - returned_entry = curr_entry; - return (Map.Entry<String, V>) curr_entry; - } - - public void remove() { - if (returned_index == -1) { - throw new IllegalStateException(); - } - - if (modCount != startModCount) { - throw new ConcurrentModificationException(); - } - - Entry<E, K, V> p = null; - Entry<E, K, V> e = arr[returned_index]; - while (e != returned_entry) { - p = e; - e = e.next; - } - if (p != null) { - p.next = returned_entry.next; - } else { - arr[returned_index] = returned_entry.next; - } - size--; - modCount++; - startModCount++; - returned_index = -1; - } - } - - private final Entry<E, K, V> findEntry(Object key1, Object key2) { - if (key1 == null && key2 == null) { - return arr[arrSize]; - } - - int hash = key1.hashCode() + key2.hashCode(); - int index = (hash & 0x7fffffff) % arrSize; - Entry<E, K, V> e = arr[index]; - - while (e != null) { - if (hash == e.hash && key1.equals(e.getKey1()) - && key2.equals(e.getKey2())) { - return e; - } - e = e.next; - } - return null; - } - - // Removes entry - private final Entry<E, K, V> removeEntry(Object key1, Object key2) { - if (key1 == null && key2 == null) { - int index = arrSize; - if (arr[index] != null) { - Entry<E, K, V> ret = arr[index]; - arr[index] = null; - size--; - modCount++; - return ret; - } - return null; - } - - int hash = key1.hashCode() + key2.hashCode(); - int index = (hash & 0x7fffffff) % arrSize; - - Entry<E, K, V> e = arr[index]; - Entry<E, K, V> prev = e; - while (e != null) { - if (hash == e.hash && key1.equals(e.getKey1()) - && key2.equals(e.getKey2())) { - if (prev == e) { - arr[index] = e.next; - } else { - prev.next = e.next; - } - size--; - modCount++; - return e; - } - - prev = e; - e = e.next; - } - return null; - } - - /** - * An instance is returned by the values() call. - */ - class ValuesCollectionImpl extends AbstractCollection<V> { - public int size() { - return size; - } - - public void clear() { - TwoKeyHashMap.this.clear(); - } - - public boolean isEmpty() { - return size == 0; - } - - public Iterator<V> iterator() { - return createValueCollectionIterator(); - } - - public boolean contains(Object obj) { - return containsValue(obj); - } - } - - class ValueIteratorImpl implements Iterator<V> { - private EntryIteratorImpl itr; - - ValueIteratorImpl() { - this.itr = new EntryIteratorImpl(); - } - - public V next() { - return itr.next().getValue(); - } - - public void remove() { - itr.remove(); - } - - public boolean hasNext() { - return itr.hasNext(); - } - } -} diff --git a/luni/src/main/java/org/apache/harmony/security/SystemScope.java b/luni/src/main/java/org/apache/harmony/security/SystemScope.java index 89cf56b..bf4f849 100644 --- a/luni/src/main/java/org/apache/harmony/security/SystemScope.java +++ b/luni/src/main/java/org/apache/harmony/security/SystemScope.java @@ -79,7 +79,7 @@ public class SystemScope extends IdentityScope { */ public synchronized Identity getIdentity(String name) { if (name == null) { - throw new NullPointerException(); + throw new NullPointerException("name == null"); } return (Identity) names.get(name); } diff --git a/luni/src/main/java/org/apache/harmony/security/fortress/Engine.java b/luni/src/main/java/org/apache/harmony/security/fortress/Engine.java index 8a67ac2..f1dd43c 100644 --- a/luni/src/main/java/org/apache/harmony/security/fortress/Engine.java +++ b/luni/src/main/java/org/apache/harmony/security/fortress/Engine.java @@ -93,15 +93,15 @@ public class Engine { /** used to test for cache hit */ private final String algorithm; /** used to test for cache validity */ - private final int refreshNumber; + private final int cacheVersion; /** cached result */ private final Provider.Service service; private ServiceCacheEntry(String algorithm, - int refreshNumber, + int cacheVersion, Provider.Service service) { this.algorithm = algorithm; - this.refreshNumber = refreshNumber; + this.cacheVersion = cacheVersion; this.service = service; } } @@ -134,12 +134,12 @@ public class Engine { if (algorithm == null) { throw new NoSuchAlgorithmException("Null algorithm name"); } - Services.refresh(); + int newCacheVersion = Services.getCacheVersion(); Provider.Service service; ServiceCacheEntry cacheEntry = this.serviceCache; if (cacheEntry != null && cacheEntry.algorithm.equalsIgnoreCase(algorithm) - && Services.refreshNumber == cacheEntry.refreshNumber) { + && newCacheVersion == cacheEntry.cacheVersion) { service = cacheEntry.service; } else { if (Services.isEmpty()) { @@ -150,7 +150,7 @@ public class Engine { if (service == null) { throw notFound(serviceName, algorithm); } - this.serviceCache = new ServiceCacheEntry(algorithm, Services.refreshNumber, service); + this.serviceCache = new ServiceCacheEntry(algorithm, newCacheVersion, service); } return new SpiAndProvider(service.newInstance(param), service.getProvider()); } diff --git a/luni/src/main/java/org/apache/harmony/security/fortress/Services.java b/luni/src/main/java/org/apache/harmony/security/fortress/Services.java index d97e0f4..4fe0d44 100644 --- a/luni/src/main/java/org/apache/harmony/security/fortress/Services.java +++ b/luni/src/main/java/org/apache/harmony/security/fortress/Services.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** -* @author Boris V. Kuznetsov -* @version $Revision$ -*/ - package org.apache.harmony.security.fortress; import java.security.Provider; @@ -34,87 +29,83 @@ import java.util.Map; /** * This class contains information about all registered providers and preferred * implementations for all "serviceName.algName". - * */ - public class Services { - // The HashMap that contains information about preferred implementations for - // all serviceName.algName in the registered providers. - // Set the initial size to 600 so we don't grow to 1024 by default because - // initialization adds a few entries more than the growth threshold. + /** + * The HashMap that contains information about preferred implementations for + * all serviceName.algName in the registered providers. + * Set the initial size to 600 so we don't grow to 1024 by default because + * initialization adds a few entries more than the growth threshold. + */ private static final Map<String, Provider.Service> services = new HashMap<String, Provider.Service>(600); - // Save default SecureRandom service as well. - // Avoids similar provider/services iteration in SecureRandom constructor - private static Provider.Service secureRandom; - // Need refresh flag - private static boolean needRefresh; // = false; + /** + * Save default SecureRandom service as well. + * Avoids similar provider/services iteration in SecureRandom constructor. + */ + private static Provider.Service cachedSecureRandomService; + + /** + * Need refresh flag. + */ + private static boolean needRefresh; /** - * Refresh number + * The cacheVersion is changed on every update of service + * information. It is used by external callers to validate their + * own caches of Service information. */ - static int refreshNumber = 1; + private static int cacheVersion = 1; - // Registered providers + /** + * Registered providers. + */ private static final List<Provider> providers = new ArrayList<Provider>(20); - // Hash for quick provider access by name + /** + * Hash for quick provider access by name. + */ private static final Map<String, Provider> providersNames = new HashMap<String, Provider>(20); static { - loadProviders(); - } - - // Load statically registered providers and init Services Info - private static void loadProviders() { String providerClassName = null; int i = 1; ClassLoader cl = ClassLoader.getSystemClassLoader(); - Provider p; - while ((providerClassName = Security.getProperty("security.provider." - + i++)) != null) { + while ((providerClassName = Security.getProperty("security.provider." + i++)) != null) { try { - p = (Provider) Class - .forName(providerClassName.trim(), true, cl) - .newInstance(); + Class providerClass = Class.forName(providerClassName.trim(), true, cl); + Provider p = (Provider) providerClass.newInstance(); providers.add(p); providersNames.put(p.getName(), p); initServiceInfo(p); - } catch (ClassNotFoundException e) { // ignore Exceptions - } catch (IllegalAccessException e) { - } catch (InstantiationException e) { + } catch (ClassNotFoundException ignored) { + } catch (IllegalAccessException ignored) { + } catch (InstantiationException ignored) { } } Engine.door.renumProviders(); } /** - * Returns registered providers - * - * @return + * Returns a copy of the registered providers as an array. */ - public static Provider[] getProviders() { + public static synchronized Provider[] getProviders() { return providers.toArray(new Provider[providers.size()]); } /** - * Returns registered providers as List - * - * @return + * Returns a copy of the registered providers as a list. */ - public static List<Provider> getProvidersList() { + public static synchronized List<Provider> getProvidersList() { return new ArrayList<Provider>(providers); } /** - * Returns the provider with the specified name - * - * @param name - * @return + * Returns the provider with the specified name. */ - public static Provider getProvider(String name) { + public static synchronized Provider getProvider(String name) { if (name == null) { return null; } @@ -122,13 +113,9 @@ public class Services { } /** - * Inserts a provider at a specified position - * - * @param provider - * @param position - * @return + * Inserts a provider at a specified 1-based position. */ - public static int insertProviderAt(Provider provider, int position) { + public static synchronized int insertProviderAt(Provider provider, int position) { int size = providers.size(); if ((position < 1) || (position > size)) { position = size + 1; @@ -140,98 +127,91 @@ public class Services { } /** - * Removes the provider - * - * @param providerNumber + * Removes the provider at the specified 1-based position. */ - public static void removeProvider(int providerNumber) { + public static synchronized void removeProvider(int providerNumber) { Provider p = providers.remove(providerNumber - 1); providersNames.remove(p.getName()); setNeedRefresh(); } /** - * * Adds information about provider services into HashMap. - * - * @param p */ - public static void initServiceInfo(Provider p) { - for (Provider.Service serv : p.getServices()) { - String type = serv.getType(); - if (secureRandom == null && type.equals("SecureRandom")) { - secureRandom = serv; + public static synchronized void initServiceInfo(Provider p) { + for (Provider.Service service : p.getServices()) { + String type = service.getType(); + if (cachedSecureRandomService == null && type.equals("SecureRandom")) { + cachedSecureRandomService = service; } - String key = type + "." + serv.getAlgorithm().toUpperCase(Locale.US); + String key = type + "." + service.getAlgorithm().toUpperCase(Locale.US); if (!services.containsKey(key)) { - services.put(key, serv); + services.put(key, service); } - for (String alias : Engine.door.getAliases(serv)) { + for (String alias : Engine.door.getAliases(service)) { key = type + "." + alias.toUpperCase(Locale.US); if (!services.containsKey(key)) { - services.put(key, serv); + services.put(key, service); } } } } /** - * - * Updates services hashtable for all registered providers - * - */ - public static void updateServiceInfo() { - services.clear(); - secureRandom = null; - for (Provider p : providers) { - initServiceInfo(p); - } - needRefresh = false; - } - - /** - * Returns true if services contain any provider information - * @return + * Returns true if services contain any provider information. */ - public static boolean isEmpty() { + public static synchronized boolean isEmpty() { return services.isEmpty(); } /** - * Returns service description. - * Call refresh() before. + * Looks up the requested service by type and algorithm. The + * service key should be provided in the same format used when + * registering a service with a provider, for example, + * "KeyFactory.RSA". * - * @param key in the format TYPE.ALGORITHM - * @return + * Callers can cache the returned service information but such + * caches should be validated against the result of + * Service.getCacheVersion() before use. */ - public static Provider.Service getService(String key) { + public static synchronized Provider.Service getService(String key) { return services.get(key); } /** * Returns the default SecureRandom service description. - * Call refresh() before. */ - public static Provider.Service getSecureRandomService() { - return secureRandom; + public static synchronized Provider.Service getSecureRandomService() { + getCacheVersion(); // used for side effect of updating cache if needed + return cachedSecureRandomService; } /** - * Set flag needRefresh - * + * In addition to being used here when the list of providers + * changes, this method is also used by the Provider + * implementation to indicate that a provides list of services has + * changed. */ - public static void setNeedRefresh() { + public static synchronized void setNeedRefresh() { needRefresh = true; } /** - * Refresh services info - * + * Returns the current cache version. This has the possible side + * effect of updating the cache if needed. */ - public static void refresh() { + public static synchronized int getCacheVersion() { if (needRefresh) { - refreshNumber++; - updateServiceInfo(); + cacheVersion++; + synchronized (services) { + services.clear(); + } + cachedSecureRandomService = null; + for (Provider p : providers) { + initServiceInfo(p); + } + needRefresh = false; } + return cacheVersion; } } diff --git a/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CRLImpl.java b/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CRLImpl.java index 134d8f9..68ec38a 100644 --- a/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CRLImpl.java +++ b/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CRLImpl.java @@ -237,7 +237,7 @@ public class X509CRLImpl extends X509CRL { */ public X509CRLEntry getRevokedCertificate(X509Certificate certificate) { if (certificate == null) { - throw new NullPointerException(); + throw new NullPointerException("certificate == null"); } if (!entriesRetrieved) { retrieveEntries(); diff --git a/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1withDSA_SignatureImpl.java b/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1withDSA_SignatureImpl.java index d2a9c6d..2958e00 100644 --- a/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1withDSA_SignatureImpl.java +++ b/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1withDSA_SignatureImpl.java @@ -57,7 +57,7 @@ public class SHA1withDSA_SignatureImpl extends Signature { protected Object engineGetParameter(String param) throws InvalidParameterException { if (param == null) { - throw new NullPointerException(); + throw new NullPointerException("param == null"); } return null; } diff --git a/luni/src/main/java/org/apache/harmony/security/x509/AuthorityKeyIdentifier.java b/luni/src/main/java/org/apache/harmony/security/x509/AuthorityKeyIdentifier.java index be43ba7..4985aff 100644 --- a/luni/src/main/java/org/apache/harmony/security/x509/AuthorityKeyIdentifier.java +++ b/luni/src/main/java/org/apache/harmony/security/x509/AuthorityKeyIdentifier.java @@ -70,6 +70,34 @@ public final class AuthorityKeyIdentifier extends ExtensionValue { return aki; } + /** + * The key identifier for the authority. + * + * @return key identifier or {@code null} + */ + public byte[] getKeyIdentifier() { + return keyIdentifier; + } + + /** + * The GeneralNames for this authority key identifier. + * + * @return names for the authority certificate issuer or {@code null} + */ + public GeneralNames getAuthorityCertIssuer() { + return authorityCertIssuer; + } + + /** + * The serial number of the certificate identified by this authority key + * identifier. + * + * @return authority's certificate serial number or {@code null} + */ + public BigInteger getAuthorityCertSerialNumber() { + return authorityCertSerialNumber; + } + @Override public byte[] getEncoded() { if (encoding == null) { encoding = ASN1.encode(this); @@ -110,10 +138,10 @@ public final class AuthorityKeyIdentifier extends ExtensionValue { @Override protected Object getDecodedObject(BerInputStream in) throws IOException { Object[] values = (Object[]) in.content; - byte[] enc = (byte[]) values[2]; + byte[] bytes = (byte[]) values[2]; BigInteger authorityCertSerialNumber = null; - if (enc != null) { - authorityCertSerialNumber = new BigInteger(enc); + if (bytes != null) { + authorityCertSerialNumber = new BigInteger(bytes); } return new AuthorityKeyIdentifier((byte[]) values[0], diff --git a/luni/src/main/java/org/apache/harmony/security/x509/SubjectKeyIdentifier.java b/luni/src/main/java/org/apache/harmony/security/x509/SubjectKeyIdentifier.java index 7415002..1db9598 100644 --- a/luni/src/main/java/org/apache/harmony/security/x509/SubjectKeyIdentifier.java +++ b/luni/src/main/java/org/apache/harmony/security/x509/SubjectKeyIdentifier.java @@ -58,6 +58,13 @@ public final class SubjectKeyIdentifier extends ExtensionValue { return res; } + /** + * The key identifier for this subject. + */ + public byte[] getKeyIdentifier() { + return keyIdentifier; + } + @Override public byte[] getEncoded() { if (encoding == null) { encoding = ASN1OctetString.getInstance().encode(keyIdentifier); diff --git a/luni/src/main/java/org/apache/harmony/xml/ExpatAttributes.java b/luni/src/main/java/org/apache/harmony/xml/ExpatAttributes.java index b92fc50..5ec632c 100644 --- a/luni/src/main/java/org/apache/harmony/xml/ExpatAttributes.java +++ b/luni/src/main/java/org/apache/harmony/xml/ExpatAttributes.java @@ -76,10 +76,10 @@ abstract class ExpatAttributes implements Attributes { public int getIndex(String uri, String localName) { if (uri == null) { - throw new NullPointerException("uri"); + throw new NullPointerException("uri == null"); } if (localName == null) { - throw new NullPointerException("local name"); + throw new NullPointerException("localName == null"); } int pointer = getPointer(); if (pointer == 0) { @@ -90,7 +90,7 @@ abstract class ExpatAttributes implements Attributes { public int getIndex(String qName) { if (qName == null) { - throw new NullPointerException("uri"); + throw new NullPointerException("qName == null"); } int pointer = getPointer(); if (pointer == 0) { @@ -101,10 +101,10 @@ abstract class ExpatAttributes implements Attributes { public String getType(String uri, String localName) { if (uri == null) { - throw new NullPointerException("uri"); + throw new NullPointerException("uri == null"); } if (localName == null) { - throw new NullPointerException("local name"); + throw new NullPointerException("localName == null"); } return getIndex(uri, localName) == -1 ? null : CDATA; } @@ -115,10 +115,10 @@ abstract class ExpatAttributes implements Attributes { public String getValue(String uri, String localName) { if (uri == null) { - throw new NullPointerException("uri"); + throw new NullPointerException("uri == null"); } if (localName == null) { - throw new NullPointerException("local name"); + throw new NullPointerException("localName == null"); } int pointer = getPointer(); if (pointer == 0) { @@ -129,7 +129,7 @@ abstract class ExpatAttributes implements Attributes { public String getValue(String qName) { if (qName == null) { - throw new NullPointerException("qName"); + throw new NullPointerException("qName == null"); } int pointer = getPointer(); if (pointer == 0) { diff --git a/luni/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java b/luni/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java index 1f1293b..af4002f 100644 --- a/luni/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java +++ b/luni/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java @@ -697,7 +697,7 @@ public abstract class NodeImpl implements Node { public final Object setUserData(String key, Object data, UserDataHandler handler) { if (key == null) { - throw new NullPointerException(); + throw new NullPointerException("key == null"); } Map<String, UserData> map = document.getUserDataMap(this); UserData previous = data == null @@ -708,7 +708,7 @@ public abstract class NodeImpl implements Node { public final Object getUserData(String key) { if (key == null) { - throw new NullPointerException(); + throw new NullPointerException("key == null"); } Map<String, UserData> map = document.getUserDataMapForRead(this); UserData userData = map.get(key); diff --git a/luni/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderFactoryImpl.java b/luni/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderFactoryImpl.java index 8efaa30..debbb20 100644 --- a/luni/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderFactoryImpl.java +++ b/luni/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderFactoryImpl.java @@ -42,7 +42,7 @@ public class DocumentBuilderFactoryImpl extends DocumentBuilderFactory { @Override public boolean getFeature(String name) throws ParserConfigurationException { if (name == null) { - throw new NullPointerException(); + throw new NullPointerException("name == null"); } if (NAMESPACES.equals(name)) { @@ -90,7 +90,7 @@ public class DocumentBuilderFactoryImpl extends DocumentBuilderFactory { public void setFeature(String name, boolean value) throws ParserConfigurationException { if (name == null) { - throw new NullPointerException(); + throw new NullPointerException("name == null"); } if (NAMESPACES.equals(name)) { diff --git a/luni/src/main/java/org/apache/harmony/xml/parsers/SAXParserFactoryImpl.java b/luni/src/main/java/org/apache/harmony/xml/parsers/SAXParserFactoryImpl.java index 08657b9..e2e3778 100644 --- a/luni/src/main/java/org/apache/harmony/xml/parsers/SAXParserFactoryImpl.java +++ b/luni/src/main/java/org/apache/harmony/xml/parsers/SAXParserFactoryImpl.java @@ -41,7 +41,7 @@ public class SAXParserFactoryImpl extends SAXParserFactory { @Override public boolean getFeature(String name) throws SAXNotRecognizedException { if (name == null) { - throw new NullPointerException(); + throw new NullPointerException("name == null"); } if (!name.startsWith("http://xml.org/sax/features/")) { @@ -86,7 +86,7 @@ public class SAXParserFactoryImpl extends SAXParserFactory { @Override public void setFeature(String name, boolean value) throws SAXNotRecognizedException { if (name == null) { - throw new NullPointerException(); + throw new NullPointerException("name == null"); } if (!name.startsWith("http://xml.org/sax/features/")) { diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/CertPinManager.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/CertPinManager.java new file mode 100644 index 0000000..4cdd8d2 --- /dev/null +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/CertPinManager.java @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xnet.provider.jsse; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.security.cert.X509Certificate; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.net.ssl.DefaultHostnameVerifier; +import libcore.io.IoUtils; +import libcore.util.BasicLruCache; + +/** + * This class provides a simple interface for cert pinning. + */ +public class CertPinManager { + + private long lastModified; + + private final Map<String, PinListEntry> entries = new HashMap<String, PinListEntry>(); + private final BasicLruCache<String, String> hostnameCache = new BasicLruCache<String, String>(10); + private final DefaultHostnameVerifier verifier = new DefaultHostnameVerifier(); + + private boolean initialized = false; + private static final boolean DEBUG = false; + + private final File pinFile; + private final TrustedCertificateStore certStore; + + public CertPinManager(TrustedCertificateStore store) throws PinManagerException { + pinFile = new File("/data/misc/keychain/pins"); + certStore = store; + rebuild(); + } + + /** Test only */ + public CertPinManager(String path, TrustedCertificateStore store) throws PinManagerException { + if (path == null) { + throw new NullPointerException("path == null"); + } + pinFile = new File(path); + certStore = store; + rebuild(); + } + + /** + * This is the public interface for cert pinning. + * + * Given a hostname and a certificate chain this verifies that the chain includes + * certs from the pinned list provided. + * + * If the chain doesn't include those certs and is in enforcing mode, then this method + * returns true and the certificate check should fail. + */ + public boolean chainIsNotPinned(String hostname, List<X509Certificate> chain) + throws PinManagerException { + // lookup the entry + PinListEntry entry = lookup(hostname); + + // return its result or false if there's no pin + if (entry != null) { + return entry.chainIsNotPinned(chain); + } + return false; + } + + private synchronized void rebuild() throws PinManagerException { + // reread the pin file + String pinFileContents = readPinFile(); + + if (pinFileContents != null) { + // rebuild the pinned certs + for (String entry : getPinFileEntries(pinFileContents)) { + try { + PinListEntry pin = new PinListEntry(entry, certStore); + entries.put(pin.getCommonName(), pin); + } catch (PinEntryException e) { + log("Pinlist contains a malformed pin: " + entry, e); + } + } + + // clear the cache + hostnameCache.evictAll(); + + // set the last modified time + lastModified = pinFile.lastModified(); + + // we've been fully initialized and are ready to go + initialized = true; + } + } + + private String readPinFile() throws PinManagerException { + try { + return IoUtils.readFileAsString(pinFile.getPath()); + } catch (FileNotFoundException e) { + // there's no pin list, all certs are unpinned + return null; + } catch (IOException e) { + // this is unexpected, fail + throw new PinManagerException("Unexpected error reading pin list; failing.", e); + } + } + + private static String[] getPinFileEntries(String pinFileContents) { + return pinFileContents.split("\n"); + } + + private synchronized PinListEntry lookup(String hostname) throws PinManagerException { + + // if we don't have any data, don't bother + if (!initialized) { + return null; + } + + // check to see if our cache is valid + if (cacheIsNotValid()) { + rebuild(); + } + + // if so, check the hostname cache + String cn = hostnameCache.get(hostname); + if (cn != null) { + // if we hit, return the corresponding entry + return entries.get(cn); + } + + // otherwise, get the matching cn + cn = getMatchingCN(hostname); + if (cn != null) { + hostnameCache.put(hostname, cn); + // we have a matching CN, return that entry + return entries.get(cn); + } + + // if we got here, we don't have a matching CN for this hostname + return null; + } + + private boolean cacheIsNotValid() { + return pinFile.lastModified() != lastModified; + } + + private String getMatchingCN(String hostname) { + String bestMatch = ""; + for (String cn : entries.keySet()) { + // skip shorter CNs since they can't be better matches + if (cn.length() < bestMatch.length()) { + continue; + } + // now verify that the CN matches at all + if (verifier.verifyHostName(hostname, cn)) { + bestMatch = cn; + } + } + return bestMatch; + } + + private static void log(String s, Exception e) { + if (DEBUG) { + System.out.println("PINFILE: " + s); + if (e != null) { + e.printStackTrace(); + } + } + } +} diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHandshakeImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHandshakeImpl.java index 4b29363..c855c0c 100644 --- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHandshakeImpl.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHandshakeImpl.java @@ -18,14 +18,12 @@ package org.apache.harmony.xnet.provider.jsse; import java.io.IOException; -import java.security.AccessController; import java.security.Key; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; -import java.security.PrivilegedExceptionAction; import java.security.PublicKey; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; @@ -39,6 +37,7 @@ import javax.crypto.spec.DHPublicKeySpec; import javax.crypto.spec.SecretKeySpec; import javax.net.ssl.X509ExtendedKeyManager; import javax.net.ssl.X509KeyManager; +import javax.net.ssl.X509TrustManager; import javax.security.auth.x500.X500Principal; /** @@ -90,7 +89,7 @@ public class ClientHandshakeImpl extends HandshakeProtocol { if (engineOwner != null) { session.setPeer(engineOwner.getPeerHost(), engineOwner.getPeerPort()); } else { - session.setPeer(socketOwner.getInetAddress().getHostName(), socketOwner.getPort()); + session.setPeer(socketOwner.getPeerHostName(), socketOwner.getPeerPort()); } session.protocol = ProtocolVersion.getLatestVersion(parameters.getEnabledProtocols()); recordProtocol.setVersion(session.protocol.version); @@ -111,7 +110,7 @@ public class ClientHandshakeImpl extends HandshakeProtocol { if (engineOwner != null) { session.setPeer(engineOwner.getPeerHost(), engineOwner.getPeerPort()); } else { - session.setPeer(socketOwner.getInetAddress().getHostName(), socketOwner.getPort()); + session.setPeer(socketOwner.getPeerHostName(), socketOwner.getPeerPort()); } session.protocol = ProtocolVersion.getLatestVersion(parameters.getEnabledProtocols()); recordProtocol.setVersion(session.protocol.version); @@ -500,7 +499,7 @@ public class ClientHandshakeImpl extends HandshakeProtocol { // send certificate verify for all certificates except those containing // fixed DH parameters - if (clientCert != null && !clientKeyExchange.isEmpty()) { + if (clientCert != null && clientCert.certs.length > 0 && !clientKeyExchange.isEmpty()) { // Certificate verify String authType = clientKey.getAlgorithm(); DigitalSignature ds = new DigitalSignature(authType); @@ -529,8 +528,21 @@ public class ClientHandshakeImpl extends HandshakeProtocol { if (authType == null) { return; } + String hostname = null; + if (engineOwner != null) { + hostname = engineOwner.getPeerHost(); + } else { + // we don't want to do an inet address lookup here in case we're talking to a proxy + hostname = socketOwner.getWrappedHostName(); + } try { - parameters.getTrustManager().checkServerTrusted(serverCert.certs, authType); + X509TrustManager x509tm = parameters.getTrustManager(); + if (x509tm instanceof TrustManagerImpl) { + TrustManagerImpl tm = (TrustManagerImpl) x509tm; + tm.checkServerTrusted(serverCert.certs, authType, hostname); + } else { + x509tm.checkServerTrusted(serverCert.certs, authType); + } } catch (CertificateException e) { fatalAlert(AlertProtocol.BAD_CERTIFICATE, "Not trusted server certificate", e); return; @@ -561,8 +573,8 @@ public class ClientHandshakeImpl extends HandshakeProtocol { host = engineOwner.getPeerHost(); port = engineOwner.getPeerPort(); } else { - host = socketOwner.getInetAddress().getHostName(); - port = socketOwner.getPort(); + host = socketOwner.getPeerHostName(); + port = socketOwner.getPeerPort(); } if (host == null || port == -1) { return null; // starts new session diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/FileClientSessionCache.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/FileClientSessionCache.java index 6619d1d..0f1fe24 100644 --- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/FileClientSessionCache.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/FileClientSessionCache.java @@ -121,7 +121,7 @@ public class FileClientSessionCache { */ private static String fileName(String host, int port) { if (host == null) { - throw new NullPointerException("host"); + throw new NullPointerException("host == null"); } return host + "." + port; } @@ -182,7 +182,7 @@ public class FileClientSessionCache { byte[] sessionData) { String host = session.getPeerHost(); if (sessionData == null) { - throw new NullPointerException("sessionData"); + throw new NullPointerException("sessionData == null"); } String name = fileName(host, session.getPeerPort()); diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java index a2688e2..2fbbef7 100644 --- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java @@ -19,8 +19,6 @@ package org.apache.harmony.xnet.provider.jsse; import java.io.PrintStream; import java.util.Locale; -import java.security.AccessController; -import java.security.PrivilegedAction; import libcore.util.EmptyArray; /** diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java index 759fc85..65373ff 100644 --- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java @@ -22,6 +22,7 @@ import java.net.SocketTimeoutException; import java.nio.ByteOrder; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.security.SignatureException; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; @@ -31,6 +32,7 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import javax.crypto.BadPaddingException; import javax.net.ssl.SSLException; import javax.security.auth.x500.X500Principal; import libcore.io.Memory; @@ -52,6 +54,8 @@ public final class NativeCrypto { public static native int ENGINE_by_id(String id); + public static native int ENGINE_add(int e); + public static native int ENGINE_init(int e); public static native int ENGINE_finish(int e); @@ -74,6 +78,8 @@ public final class NativeCrypto { public static native void EVP_PKEY_free(int pkey); + public static native int EVP_PKEY_cmp(int pkey1, int pkey2); + public static native byte[] i2d_PKCS8_PRIV_KEY_INFO(int pkey); public static native int d2i_PKCS8_PRIV_KEY_INFO(byte[] data); @@ -84,6 +90,20 @@ public final class NativeCrypto { public static native int RSA_generate_key_ex(int modulusBits, byte[] publicExponent); + public static native int RSA_size(int pkey); + + public static native int RSA_private_encrypt(int flen, byte[] from, byte[] to, int pkey, + int padding); + + public static native int RSA_public_decrypt(int flen, byte[] from, byte[] to, int pkey, + int padding) throws BadPaddingException, SignatureException; + + public static native int RSA_public_encrypt(int flen, byte[] from, byte[] to, int pkey, + int padding); + + public static native int RSA_private_decrypt(int flen, byte[] from, byte[] to, int pkey, + int padding) throws BadPaddingException, SignatureException; + /** * @return array of {n, e} */ @@ -172,6 +192,8 @@ public final class NativeCrypto { public static native int RAND_load_file(String filename, long max_bytes); + public static native void RAND_bytes(byte[] output); + // --- X509_NAME ----------------------------------------------------------- public static int X509_NAME_hash(X500Principal principal) { @@ -333,13 +355,16 @@ public final class NativeCrypto { public static final int EVP_PKEY_DH = 28; // NID_dhKeyAgreement public static final int EVP_PKEY_EC = 408; // NID_X9_62_id_ecPublicKey + // RSA padding modes from rsa.h + public static final int RSA_PKCS1_PADDING = 1; + public static final int RSA_NO_PADDING = 3; + // SSL mode from ssl.h public static final long SSL_MODE_HANDSHAKE_CUTTHROUGH = 0x00000040L; // SSL options from ssl.h public static final long SSL_OP_NO_TICKET = 0x00004000L; public static final long SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION = 0x00010000L; - public static final long SSL_OP_NO_COMPRESSION = 0x00020000L; public static final long SSL_OP_NO_SSLv3 = 0x02000000L; public static final long SSL_OP_NO_TLSv1 = 0x04000000L; public static final long SSL_OP_NO_TLSv1_1 = 0x10000000L; @@ -544,66 +569,6 @@ public final class NativeCrypto { return cipherSuites; } - public static final String SUPPORTED_COMPRESSION_METHOD_ZLIB = "ZLIB"; - public static final String SUPPORTED_COMPRESSION_METHOD_NULL = "NULL"; - - private static final String[] SUPPORTED_COMPRESSION_METHODS - = { SUPPORTED_COMPRESSION_METHOD_ZLIB, SUPPORTED_COMPRESSION_METHOD_NULL }; - - public static String[] getSupportedCompressionMethods() { - return SUPPORTED_COMPRESSION_METHODS.clone(); - } - - public static final String[] getDefaultCompressionMethods() { - return new String[] { SUPPORTED_COMPRESSION_METHOD_NULL }; - } - - public static String[] checkEnabledCompressionMethods(String[] methods) { - if (methods == null) { - throw new IllegalArgumentException("methods == null"); - } - if (methods.length < 1 - && !methods[methods.length-1].equals(SUPPORTED_COMPRESSION_METHOD_NULL)) { - throw new IllegalArgumentException("last method must be NULL"); - } - for (int i = 0; i < methods.length; i++) { - String method = methods[i]; - if (method == null) { - throw new IllegalArgumentException("methods[" + i + "] == null"); - } - if (!method.equals(SUPPORTED_COMPRESSION_METHOD_ZLIB) - && !method.equals(SUPPORTED_COMPRESSION_METHOD_NULL)) { - throw new IllegalArgumentException("method " + method - + " is not supported"); - } - } - return methods; - } - - public static void setEnabledCompressionMethods(int ssl, String[] methods) { - checkEnabledCompressionMethods(methods); - // openssl uses negative logic letting you disable compression. - // so first, assume we need to set all (disable all) and clear none (enable none). - // in the loop, selectively move bits from set to clear (from disable to enable) - long optionsToSet = (SSL_OP_NO_COMPRESSION); - long optionsToClear = 0; - for (int i = 0; i < methods.length; i++) { - String method = methods[i]; - if (method.equals(SUPPORTED_COMPRESSION_METHOD_NULL)) { - // nothing to do to support NULL - } else if (method.equals(SUPPORTED_COMPRESSION_METHOD_ZLIB)) { - optionsToSet &= ~SSL_OP_NO_COMPRESSION; - optionsToClear |= SSL_OP_NO_COMPRESSION; - } else { - // error checked by checkEnabledCompressionMethods - throw new IllegalStateException(); - } - } - - SSL_set_options(ssl, optionsToSet); - SSL_clear_options(ssl, optionsToClear); - } - /* * See the OpenSSL ssl.h header file for more information. */ @@ -680,7 +645,7 @@ public final class NativeCrypto { public static native int SSL_read(int sslNativePointer, FileDescriptor fd, SSLHandshakeCallbacks shc, - byte[] b, int off, int len, int timeoutMillis) + byte[] b, int off, int len, int readTimeoutMillis) throws IOException; /** @@ -689,7 +654,7 @@ public final class NativeCrypto { public static native void SSL_write(int sslNativePointer, FileDescriptor fd, SSLHandshakeCallbacks shc, - byte[] b, int off, int len) + byte[] b, int off, int len, int writeTimeoutMillis) throws IOException; public static native void SSL_interrupt(int sslNativePointer); @@ -707,9 +672,6 @@ public final class NativeCrypto { public static native String SSL_SESSION_cipher(int sslSessionNativePointer); - public static native String SSL_SESSION_compress_meth(int sslCtxNativePointer, - int sslSessionNativePointer); - public static native void SSL_SESSION_free(int sslSessionNativePointer); public static native byte[] i2d_SSL_SESSION(int sslSessionNativePointer); diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLCipherRSA.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLCipherRSA.java new file mode 100644 index 0000000..ddf2e0d --- /dev/null +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLCipherRSA.java @@ -0,0 +1,356 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xnet.provider.jsse; + +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.InvalidParameterException; +import java.security.Key; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.SignatureException; +import java.security.interfaces.RSAPrivateCrtKey; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Arrays; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.CipherSpi; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.SecretKeySpec; +import libcore.util.EmptyArray; + +public abstract class OpenSSLCipherRSA extends CipherSpi { + /** + * The current OpenSSL key we're operating on. + */ + private OpenSSLKey key; + + /** + * Current key type: private or public. + */ + private boolean usingPrivateKey; + + /** + * Current cipher mode: encrypting or decrypting. + */ + private boolean encrypting; + + /** + * Buffer for operations + */ + private byte[] buffer; + + /** + * Current offset in the buffer. + */ + private int bufferOffset; + + /** + * Flag that indicates an exception should be thrown when the input is too + * large during doFinal. + */ + private boolean inputTooLarge; + + /** + * Current padding mode + */ + private int padding = NativeCrypto.RSA_PKCS1_PADDING; + + protected OpenSSLCipherRSA(int padding) { + this.padding = padding; + } + + @Override + protected void engineSetMode(String mode) throws NoSuchAlgorithmException { + final String modeUpper = mode.toUpperCase(); + if ("NONE".equals(modeUpper) || "ECB".equals(modeUpper)) { + return; + } + + throw new NoSuchAlgorithmException("mode not supported: " + mode); + } + + @Override + protected void engineSetPadding(String padding) throws NoSuchPaddingException { + final String paddingUpper = padding.toUpperCase(); + if ("PKCS1PADDING".equals(paddingUpper)) { + this.padding = NativeCrypto.RSA_PKCS1_PADDING; + return; + } + if ("NOPADDING".equals(paddingUpper)) { + this.padding = NativeCrypto.RSA_NO_PADDING; + return; + } + + throw new NoSuchPaddingException("padding not supported: " + padding); + } + + @Override + protected int engineGetBlockSize() { + if (encrypting) { + return paddedBlockSizeBytes(); + } + return keySizeBytes(); + } + + @Override + protected int engineGetOutputSize(int inputLen) { + if (encrypting) { + return keySizeBytes(); + } + return paddedBlockSizeBytes(); + } + + private int paddedBlockSizeBytes() { + int paddedBlockSizeBytes = keySizeBytes(); + if (padding == NativeCrypto.RSA_PKCS1_PADDING) { + paddedBlockSizeBytes--; // for 0 prefix + paddedBlockSizeBytes -= 10; // PKCS1 padding header length + } + return paddedBlockSizeBytes; + } + + private int keySizeBytes() { + if (key == null) { + throw new IllegalStateException("cipher is not initialized"); + } + return NativeCrypto.RSA_size(this.key.getPkeyContext()); + } + + @Override + protected byte[] engineGetIV() { + return null; + } + + @Override + protected AlgorithmParameters engineGetParameters() { + return null; + } + + private void engineInitInternal(int opmode, Key key) throws InvalidKeyException { + if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE) { + encrypting = true; + } else if (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE) { + encrypting = false; + } else { + throw new InvalidParameterException("Unsupported opmode " + opmode); + } + + if (key instanceof OpenSSLRSAPrivateKey) { + OpenSSLRSAPrivateKey rsaPrivateKey = (OpenSSLRSAPrivateKey) key; + usingPrivateKey = true; + this.key = rsaPrivateKey.getOpenSSLKey(); + } else if (key instanceof RSAPrivateCrtKey) { + RSAPrivateCrtKey rsaPrivateKey = (RSAPrivateCrtKey) key; + usingPrivateKey = true; + this.key = OpenSSLRSAPrivateCrtKey.getInstance(rsaPrivateKey); + } else if (key instanceof RSAPrivateKey) { + RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) key; + usingPrivateKey = true; + this.key = OpenSSLRSAPrivateKey.getInstance(rsaPrivateKey); + } else if (key instanceof OpenSSLRSAPublicKey) { + OpenSSLRSAPublicKey rsaPublicKey = (OpenSSLRSAPublicKey) key; + usingPrivateKey = false; + this.key = rsaPublicKey.getOpenSSLKey(); + } else if (key instanceof RSAPublicKey) { + RSAPublicKey rsaPublicKey = (RSAPublicKey) key; + usingPrivateKey = false; + this.key = OpenSSLRSAPublicKey.getInstance(rsaPublicKey); + } else { + throw new InvalidKeyException("Need RSA private or public key"); + } + + buffer = new byte[NativeCrypto.RSA_size(this.key.getPkeyContext())]; + inputTooLarge = false; + } + + @Override + protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException { + engineInitInternal(opmode, key); + } + + @Override + protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, + SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { + if (params != null) { + throw new InvalidAlgorithmParameterException("unknown param type: " + + params.getClass().getName()); + } + + engineInitInternal(opmode, key); + } + + @Override + protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + if (params != null) { + throw new InvalidAlgorithmParameterException("unknown param type: " + + params.getClass().getName()); + } + + engineInitInternal(opmode, key); + } + + @Override + protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { + if (bufferOffset + inputLen > buffer.length) { + inputTooLarge = true; + return EmptyArray.BYTE; + } + + System.arraycopy(input, inputOffset, buffer, bufferOffset, inputLen); + bufferOffset += inputLen; + return EmptyArray.BYTE; + } + + @Override + protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, + int outputOffset) throws ShortBufferException { + engineUpdate(input, inputOffset, inputLen); + return 0; + } + + @Override + protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) + throws IllegalBlockSizeException, BadPaddingException { + if (input != null) { + engineUpdate(input, inputOffset, inputLen); + } + + if (inputTooLarge) { + throw new IllegalBlockSizeException("input must be under " + buffer.length + " bytes"); + } + + final byte[] tmpBuf; + if (bufferOffset != buffer.length) { + if (padding == NativeCrypto.RSA_NO_PADDING) { + tmpBuf = new byte[buffer.length]; + System.arraycopy(buffer, 0, tmpBuf, buffer.length - bufferOffset, bufferOffset); + } else { + tmpBuf = Arrays.copyOf(buffer, bufferOffset); + } + } else { + tmpBuf = buffer; + } + + byte[] output = new byte[buffer.length]; + int resultSize; + if (encrypting) { + if (usingPrivateKey) { + resultSize = NativeCrypto.RSA_private_encrypt(tmpBuf.length, tmpBuf, output, + key.getPkeyContext(), padding); + } else { + resultSize = NativeCrypto.RSA_public_encrypt(tmpBuf.length, tmpBuf, output, + key.getPkeyContext(), padding); + } + } else { + try { + if (usingPrivateKey) { + resultSize = NativeCrypto.RSA_private_decrypt(tmpBuf.length, tmpBuf, output, + key.getPkeyContext(), padding); + } else { + resultSize = NativeCrypto.RSA_public_decrypt(tmpBuf.length, tmpBuf, output, + key.getPkeyContext(), padding); + } + } catch (SignatureException e) { + IllegalBlockSizeException newE = new IllegalBlockSizeException(); + newE.initCause(e); + throw newE; + } + } + if (!encrypting && resultSize != output.length) { + output = Arrays.copyOf(output, resultSize); + } + + bufferOffset = 0; + return output; + } + + @Override + protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, + int outputOffset) throws ShortBufferException, IllegalBlockSizeException, + BadPaddingException { + byte[] b = engineDoFinal(input, inputOffset, inputLen); + + final int lastOffset = outputOffset + b.length; + if (lastOffset > output.length) { + throw new ShortBufferException("output buffer is too small " + output.length + " < " + + lastOffset); + } + + System.arraycopy(b, 0, output, outputOffset, b.length); + return b.length; + } + + @Override + protected byte[] engineWrap(Key key) throws IllegalBlockSizeException, InvalidKeyException { + try { + byte[] encoded = key.getEncoded(); + return engineDoFinal(encoded, 0, encoded.length); + } catch (BadPaddingException e) { + IllegalBlockSizeException newE = new IllegalBlockSizeException(); + newE.initCause(e); + throw newE; + } + } + + @Override + protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, + int wrappedKeyType) throws InvalidKeyException, NoSuchAlgorithmException { + try { + byte[] encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length); + if (wrappedKeyType == Cipher.PUBLIC_KEY) { + KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm); + return keyFactory.generatePublic(new X509EncodedKeySpec(encoded)); + } else if (wrappedKeyType == Cipher.PRIVATE_KEY) { + KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm); + return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encoded)); + } else if (wrappedKeyType == Cipher.SECRET_KEY) { + return new SecretKeySpec(encoded, wrappedKeyAlgorithm); + } else { + throw new UnsupportedOperationException("wrappedKeyType == " + wrappedKeyType); + } + } catch (IllegalBlockSizeException e) { + throw new InvalidKeyException(e); + } catch (BadPaddingException e) { + throw new InvalidKeyException(e); + } catch (InvalidKeySpecException e) { + throw new InvalidKeyException(e); + } + } + + public static class PKCS1 extends OpenSSLCipherRSA { + public PKCS1() { + super(NativeCrypto.RSA_PKCS1_PADDING); + } + } + + public static class Raw extends OpenSSLCipherRSA { + public Raw() { + super(NativeCrypto.RSA_NO_PADDING); + } + } +} diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAPrivateKey.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAPrivateKey.java index 7cd16f7..761b08e 100644 --- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAPrivateKey.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLDSAPrivateKey.java @@ -120,6 +120,10 @@ public class OpenSSLDSAPrivateKey implements DSAPrivateKey { return key.getPkeyContext(); } + public String getPkeyAlias() { + return key.getAlias(); + } + @Override public boolean equals(Object o) { if (o == this) { diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLEngine.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLEngine.java index e91e6d8..d01dc62 100644 --- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLEngine.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLEngine.java @@ -24,6 +24,8 @@ public class OpenSSLEngine { NativeCrypto.ENGINE_load_dynamic(); } + private static final Object mLoadingLock = new Object(); + /** The ENGINE's native handle. */ private final int ctx; @@ -32,10 +34,14 @@ public class OpenSSLEngine { throw new NullPointerException("engine == null"); } - final int engineCtx = NativeCrypto.ENGINE_by_id(engine); + final int engineCtx; + synchronized (mLoadingLock) { + engineCtx = NativeCrypto.ENGINE_by_id(engine); + if (engineCtx == 0) { + throw new IllegalArgumentException("Unknown ENGINE id: " + engine); + } - if (engineCtx == 0) { - throw new IllegalArgumentException("Unknown ENGINE id: " + engine); + NativeCrypto.ENGINE_add(engineCtx); } return new OpenSSLEngine(engineCtx); @@ -45,6 +51,7 @@ public class OpenSSLEngine { ctx = engineCtx; if (NativeCrypto.ENGINE_init(engineCtx) == 0) { + NativeCrypto.ENGINE_free(engineCtx); throw new IllegalArgumentException("Could not initialize engine"); } } @@ -62,9 +69,9 @@ public class OpenSSLEngine { final int keyType = NativeCrypto.EVP_PKEY_type(keyRef); switch (keyType) { case NativeCrypto.EVP_PKEY_RSA: - return OpenSSLRSAPrivateKey.getInstance(new OpenSSLKey(keyRef, this)); + return OpenSSLRSAPrivateKey.getInstance(new OpenSSLKey(keyRef, this, id)); case NativeCrypto.EVP_PKEY_DSA: - return new OpenSSLDSAPrivateKey(new OpenSSLKey(keyRef, this)); + return new OpenSSLDSAPrivateKey(new OpenSSLKey(keyRef, this, id)); default: throw new InvalidKeyException("Unknown key type: " + keyType); } diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLKey.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLKey.java index 90eb0e2..b8b9f69 100644 --- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLKey.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLKey.java @@ -21,14 +21,18 @@ class OpenSSLKey { private final OpenSSLEngine engine; + private final String alias; + OpenSSLKey(int ctx) { this.ctx = ctx; engine = null; + alias = null; } - OpenSSLKey(int ctx, OpenSSLEngine engine) { + OpenSSLKey(int ctx, OpenSSLEngine engine, String alias) { this.ctx = ctx; this.engine = engine; + this.alias = alias; } int getPkeyContext() { @@ -43,6 +47,10 @@ class OpenSSLKey { return engine != null; } + String getAlias() { + return alias; + } + @Override protected void finalize() throws Throwable { try { diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLProvider.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLProvider.java index 97753cf..d4aa57f 100644 --- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLProvider.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLProvider.java @@ -73,33 +73,32 @@ public final class OpenSSLProvider extends Provider { // put("KeyFactory.DSA", OpenSSLDSAKeyFactory.class.getName()); // Signatures - put("Signature.MD5WithRSAEncryption", OpenSSLSignature.MD5RSA.class.getName()); - put("Alg.Alias.Signature.MD5WithRSA", "MD5WithRSAEncryption"); - put("Alg.Alias.Signature.MD5/RSA", "MD5WithRSAEncryption"); - put("Alg.Alias.Signature.1.2.840.113549.1.1.4", "MD5WithRSAEncryption"); - put("Alg.Alias.Signature.1.2.840.113549.2.5with1.2.840.113549.1.1.1", - "MD5WithRSAEncryption"); - - put("Signature.SHA1WithRSAEncryption", OpenSSLSignature.SHA1RSA.class.getName()); - put("Alg.Alias.Signature.SHA1WithRSA", "SHA1WithRSAEncryption"); - put("Alg.Alias.Signature.SHA1/RSA", "SHA1WithRSAEncryption"); - put("Alg.Alias.Signature.SHA-1/RSA", "SHA1WithRSAEncryption"); - put("Alg.Alias.Signature.1.2.840.113549.1.1.5", "SHA1WithRSAEncryption"); - put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.1", "SHA1WithRSAEncryption"); - put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.5", "SHA1WithRSAEncryption"); - put("Alg.Alias.Signature.1.3.14.3.2.29", "SHA1WithRSAEncryption"); - - put("Signature.SHA256WithRSAEncryption", OpenSSLSignature.SHA256RSA.class.getName()); - put("Alg.Alias.Signature.SHA256WithRSA", "SHA256WithRSAEncryption"); - put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA256WithRSAEncryption"); - - put("Signature.SHA384WithRSAEncryption", OpenSSLSignature.SHA384RSA.class.getName()); - put("Alg.Alias.Signature.SHA384WithRSA", "SHA384WithRSAEncryption"); - put("Alg.Alias.Signature.1.2.840.113549.1.1.12", "SHA384WithRSAEncryption"); - - put("Signature.SHA512WithRSAEncryption", OpenSSLSignature.SHA512RSA.class.getName()); - put("Alg.Alias.Signature.SHA512WithRSA", "SHA512WithRSAEncryption"); - put("Alg.Alias.Signature.1.2.840.113549.1.1.13", "SHA512WithRSAEncryption"); + put("Signature.MD5WithRSA", OpenSSLSignature.MD5RSA.class.getName()); + put("Alg.Alias.Signature.MD5WithRSAEncryption", "MD5WithRSA"); + put("Alg.Alias.Signature.MD5/RSA", "MD5WithRSA"); + put("Alg.Alias.Signature.1.2.840.113549.1.1.4", "MD5WithRSA"); + put("Alg.Alias.Signature.1.2.840.113549.2.5with1.2.840.113549.1.1.1", "MD5WithRSA"); + + put("Signature.SHA1WithRSA", OpenSSLSignature.SHA1RSA.class.getName()); + put("Alg.Alias.Signature.SHA1WithRSA", "SHA1WithRSA"); + put("Alg.Alias.Signature.SHA1/RSA", "SHA1WithRSA"); + put("Alg.Alias.Signature.SHA-1/RSA", "SHA1WithRSA"); + put("Alg.Alias.Signature.1.2.840.113549.1.1.5", "SHA1WithRSA"); + put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.1", "SHA1WithRSA"); + put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.5", "SHA1WithRSA"); + put("Alg.Alias.Signature.1.3.14.3.2.29", "SHA1WithRSA"); + + put("Signature.SHA256WithRSA", OpenSSLSignature.SHA256RSA.class.getName()); + put("Alg.Alias.Signature.SHA256WithRSAEncryption", "SHA256WithRSA"); + put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA256WithRSA"); + + put("Signature.SHA384WithRSA", OpenSSLSignature.SHA384RSA.class.getName()); + put("Alg.Alias.Signature.SHA384WithRSAEncryption", "SHA384WithRSA"); + put("Alg.Alias.Signature.1.2.840.113549.1.1.12", "SHA384WithRSA"); + + put("Signature.SHA512WithRSA", OpenSSLSignature.SHA512RSA.class.getName()); + put("Alg.Alias.Signature.SHA512WithRSAEncryption", "SHA512WithRSA"); + put("Alg.Alias.Signature.1.2.840.113549.1.1.13", "SHA512WithRSA"); put("Signature.SHA1withDSA", OpenSSLSignature.SHA1DSA.class.getName()); put("Alg.Alias.Signature.SHA/DSA", "SHA1withDSA"); @@ -108,5 +107,22 @@ public final class OpenSSLProvider extends Provider { put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.10040.4.3", "SHA1withDSA"); put("Alg.Alias.Signature.DSAWithSHA1", "SHA1withDSA"); put("Alg.Alias.Signature.1.2.840.10040.4.3", "SHA1withDSA"); + + put("Signature.NONEwithRSA", OpenSSLSignatureRawRSA.class.getName()); + + // SecureRandom + /* + * We have to specify SHA1PRNG because various documentation mentions + * that algorithm by name instead of just recommending calling + * "new SecureRandom()" + */ + put("SecureRandom.SHA1PRNG", OpenSSLRandom.class.getName()); + put("SecureRandom.SHA1PRNG ImplementedIn", "Software"); + + // Cipher + put("Cipher.RSA/ECB/NoPadding", OpenSSLCipherRSA.Raw.class.getName()); + put("Alg.Alias.Cipher.RSA/None/NoPadding", "RSA/ECB/NoPadding"); + put("Cipher.RSA/ECB/PKCS1Padding", OpenSSLCipherRSA.PKCS1.class.getName()); + put("Alg.Alias.Cipher.RSA/None/PKCS1Padding", "RSA/ECB/PKCS1Padding"); } } diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateCrtKey.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateCrtKey.java index 8376515..4303e5a 100644 --- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateCrtKey.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateCrtKey.java @@ -191,8 +191,8 @@ public class OpenSSLRSAPrivateCrtKey extends OpenSSLRSAPrivateKey implements RSA return true; } - if (o instanceof OpenSSLRSAPrivateCrtKey) { - OpenSSLRSAPrivateCrtKey other = (OpenSSLRSAPrivateCrtKey) o; + if (o instanceof OpenSSLRSAPrivateKey) { + OpenSSLRSAPrivateKey other = (OpenSSLRSAPrivateKey) o; /* * We can shortcut the true case, but it still may be equivalent but @@ -201,19 +201,36 @@ public class OpenSSLRSAPrivateCrtKey extends OpenSSLRSAPrivateKey implements RSA if (getOpenSSLKey().equals(other.getOpenSSLKey())) { return true; } + + return NativeCrypto.EVP_PKEY_cmp(getPkeyContext(), other.getPkeyContext()) == 1; } if (o instanceof RSAPrivateCrtKey) { ensureReadParams(); RSAPrivateCrtKey other = (RSAPrivateCrtKey) o; - return getModulus().equals(other.getModulus()) - && publicExponent.equals(other.getPublicExponent()) - && getPrivateExponent().equals(other.getPrivateExponent()) - && primeP.equals(other.getPrimeP()) && primeQ.equals(other.getPrimeQ()) - && primeExponentP.equals(other.getPrimeExponentP()) - && primeExponentQ.equals(other.getPrimeExponentQ()) - && crtCoefficient.equals(other.getCrtCoefficient()); + if (getOpenSSLKey().isEngineBased()) { + return getModulus().equals(other.getModulus()) + && publicExponent.equals(other.getPublicExponent()); + } else { + return getModulus().equals(other.getModulus()) + && publicExponent.equals(other.getPublicExponent()) + && getPrivateExponent().equals(other.getPrivateExponent()) + && primeP.equals(other.getPrimeP()) && primeQ.equals(other.getPrimeQ()) + && primeExponentP.equals(other.getPrimeExponentP()) + && primeExponentQ.equals(other.getPrimeExponentQ()) + && crtCoefficient.equals(other.getCrtCoefficient()); + } + } else if (o instanceof RSAPrivateKey) { + ensureReadParams(); + RSAPrivateKey other = (RSAPrivateKey) o; + + if (getOpenSSLKey().isEngineBased()) { + return getModulus().equals(other.getModulus()); + } else { + return getModulus().equals(other.getModulus()) + && getPrivateExponent().equals(other.getPrivateExponent()); + } } return false; @@ -232,11 +249,11 @@ public class OpenSSLRSAPrivateCrtKey extends OpenSSLRSAPrivateKey implements RSA public String toString() { final StringBuilder sb = new StringBuilder("OpenSSLRSAPrivateCrtKey{"); - if (getOpenSSLKey().isEngineBased()) { + final boolean engineBased = getOpenSSLKey().isEngineBased(); + if (engineBased) { sb.append("key="); sb.append(getOpenSSLKey()); sb.append('}'); - return sb.toString(); } ensureReadParams(); @@ -250,9 +267,11 @@ public class OpenSSLRSAPrivateCrtKey extends OpenSSLRSAPrivateKey implements RSA sb.append(','); } - sb.append("privateExponent="); - sb.append(getPrivateExponent().toString(16)); - sb.append(','); + if (!engineBased) { + sb.append("privateExponent="); + sb.append(getPrivateExponent().toString(16)); + sb.append(','); + } if (primeP != null) { sb.append("primeP="); diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateKey.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateKey.java index c9fa178..adb05a9 100644 --- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateKey.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRSAPrivateKey.java @@ -180,6 +180,10 @@ public class OpenSSLRSAPrivateKey implements RSAPrivateKey { return key.getPkeyContext(); } + public String getPkeyAlias() { + return key.getAlias(); + } + @Override public boolean equals(Object o) { if (o == this) { @@ -196,6 +200,8 @@ public class OpenSSLRSAPrivateKey implements RSAPrivateKey { if (key.equals(other.getOpenSSLKey())) { return true; } + + return NativeCrypto.EVP_PKEY_cmp(getPkeyContext(), other.getPkeyContext()) == 1; } if (o instanceof RSAPrivateKey) { @@ -226,11 +232,11 @@ public class OpenSSLRSAPrivateKey implements RSAPrivateKey { public String toString() { final StringBuilder sb = new StringBuilder("OpenSSLRSAPrivateKey{"); - if (key.isEngineBased()) { + final boolean engineBased = key.isEngineBased(); + if (engineBased) { sb.append("key="); sb.append(key); sb.append('}'); - return sb.toString(); } ensureReadParams(); @@ -238,9 +244,11 @@ public class OpenSSLRSAPrivateKey implements RSAPrivateKey { sb.append(modulus.toString(16)); sb.append(','); - sb.append("privateExponent="); - sb.append(privateExponent.toString(16)); - sb.append(','); + if (!engineBased) { + sb.append("privateExponent="); + sb.append(privateExponent.toString(16)); + sb.append(','); + } return sb.toString(); } diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRandom.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRandom.java new file mode 100644 index 0000000..fd011f0 --- /dev/null +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLRandom.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xnet.provider.jsse; + +import java.io.Serializable; +import java.security.SecureRandomSpi; + +public class OpenSSLRandom extends SecureRandomSpi implements Serializable { + private static final long serialVersionUID = 8506210602917522860L; + + @Override + protected void engineSetSeed(byte[] seed) { + NativeCrypto.RAND_seed(seed); + } + + @Override + protected void engineNextBytes(byte[] bytes) { + NativeCrypto.RAND_bytes(bytes); + } + + @Override + protected byte[] engineGenerateSeed(int numBytes) { + byte[] output = new byte[numBytes]; + NativeCrypto.RAND_bytes(output); + return output; + } +} diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java index 841c31c..2f5fe59 100644 --- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java @@ -32,7 +32,6 @@ public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket { private final SSLParametersImpl sslParameters; private String[] enabledProtocols = NativeCrypto.getSupportedProtocols(); private String[] enabledCipherSuites = NativeCrypto.getDefaultCipherSuites(); - private String[] enabledCompressionMethods = NativeCrypto.getDefaultCompressionMethods(); protected OpenSSLServerSocketImpl(SSLParametersImpl sslParameters) throws IOException { this.sslParameters = sslParameters; @@ -126,26 +125,6 @@ public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket { enabledCipherSuites = NativeCrypto.checkEnabledCipherSuites(suites); } - public String[] getSupportedCompressionMethods() { - return NativeCrypto.getSupportedCompressionMethods(); - } - - public String[] getEnabledCompressionMethods() { - return enabledCompressionMethods.clone(); - } - - /** - * This method enables the compression methods listed by - * getSupportedCompressionMethods(). - * - * @param suites the names of all the compression methods to enable - * @throws IllegalArgumentException when one or more of the ciphers in array - * suites are not supported, or when the array is null. - */ - public void setEnabledCompressionMethods(String[] methods) { - enabledCompressionMethods = NativeCrypto.checkEnabledCompressionMethods(methods); - } - @Override public boolean getWantClientAuth() { return sslParameters.getWantClientAuth(); @@ -185,8 +164,7 @@ public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket { OpenSSLSocketImpl socket = new OpenSSLSocketImpl(sslParameters, enabledProtocols.clone(), - enabledCipherSuites.clone(), - enabledCompressionMethods.clone()); + enabledCipherSuites.clone()); implAccept(socket); return socket; } diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java index e194a38..003122f 100644 --- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java @@ -49,7 +49,6 @@ public class OpenSSLSessionImpl implements SSLSession { private int peerPort = -1; private String cipherSuite; private String protocol; - private String compressionMethod; private AbstractSessionContext sessionContext; private byte[] id; @@ -328,19 +327,6 @@ public class OpenSSLSessionImpl implements SSLSession { } /** - * Returns the compression method name used in all connections - * pertaining to this SSL session. - */ - public String getCompressionMethod() { - if (compressionMethod == null) { - compressionMethod - = NativeCrypto.SSL_SESSION_compress_meth(sessionContext.sslCtxNativePointer, - sslSessionNativePointer); - } - return compressionMethod; - } - - /** * Returns the context to which the actual SSL session is bound. A SSL * context consists of (1) a possible delegate, (2) a provider and (3) a * protocol. @@ -380,9 +366,7 @@ public class OpenSSLSessionImpl implements SSLSession { /** * Returns the object which is bound to the the input parameter name. * This name is a sort of link to the data of the SSL session's application - * layer, if any exists. The search for this link is monitored, as a matter - * of security, by the full machinery of the <code>AccessController</code> - * class. + * layer, if any exists. * * @param name the name of the binding to find. * @return the value bound to that name, or null if the binding does not @@ -398,9 +382,7 @@ public class OpenSSLSessionImpl implements SSLSession { /** * Returns an array with the names (sort of links) of all the data - * objects of the application layer bound into the SSL session. The search - * for this link is monitored, as a matter of security, by the full - * machinery of the <code>AccessController</code> class. + * objects of the application layer bound into the SSL session. * * @return a non-null (possibly empty) array of names of the data objects * bound to this SSL session. @@ -413,9 +395,7 @@ public class OpenSSLSessionImpl implements SSLSession { * A link (name) with the specified value object of the SSL session's * application layer data is created or replaced. If the new (or existing) * value object implements the <code>SSLSessionBindingListener</code> - * interface, that object will be notified in due course. These links-to - * -data bounds are monitored, as a matter of security, by the full - * machinery of the <code>AccessController</code> class. + * interface, that object will be notified in due course. * * @param name the name of the link (no null are * accepted!) @@ -446,10 +426,6 @@ public class OpenSSLSessionImpl implements SSLSession { * <p>If the value object implements the <code>SSLSessionBindingListener</code> * interface, the object will receive a <code>valueUnbound</code> notification. * - * <p>These links-to -data bounds are - * monitored, as a matter of security, by the full machinery of the - * <code>AccessController</code> class. - * * @param name the name of the link (no null are * accepted!) * @throws <code>IllegalArgumentException</code> if the argument is null. diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSignatureRawRSA.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSignatureRawRSA.java new file mode 100644 index 0000000..289af30 --- /dev/null +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSignatureRawRSA.java @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xnet.provider.jsse; + +import java.security.InvalidKeyException; +import java.security.InvalidParameterException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; +import java.security.SignatureException; +import java.security.interfaces.RSAPrivateCrtKey; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.util.Arrays; + +/** + * Implements the JDK Signature interface needed for RAW RSA signature + * generation and verification using OpenSSL. + */ +public class OpenSSLSignatureRawRSA extends Signature { + /** + * The current OpenSSL key we're operating on. + */ + private OpenSSLKey key; + + /** + * Buffer to hold value to be signed or verified. + */ + private byte[] inputBuffer; + + /** + * Current offset in input buffer. + */ + private int inputOffset; + + /** + * Provides a flag to specify when the input is too long. + */ + private boolean inputIsTooLong; + + /** + * Creates a new OpenSSLSignature instance for the given algorithm name. + */ + public OpenSSLSignatureRawRSA() throws NoSuchAlgorithmException { + super("NONEwithRSA"); + } + + @Override + protected void engineUpdate(byte input) { + final int oldOffset = inputOffset++; + + if (inputOffset > inputBuffer.length) { + inputIsTooLong = true; + return; + } + + inputBuffer[oldOffset] = input; + } + + @Override + protected void engineUpdate(byte[] input, int offset, int len) { + final int oldOffset = inputOffset; + inputOffset += len; + + if (inputOffset > inputBuffer.length) { + inputIsTooLong = true; + return; + } + + System.arraycopy(input, offset, inputBuffer, oldOffset, len); + } + + @Override + protected Object engineGetParameter(String param) throws InvalidParameterException { + return null; + } + + @Override + protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException { + if (privateKey instanceof OpenSSLRSAPrivateKey) { + OpenSSLRSAPrivateKey rsaPrivateKey = (OpenSSLRSAPrivateKey) privateKey; + key = rsaPrivateKey.getOpenSSLKey(); + } else if (privateKey instanceof RSAPrivateCrtKey) { + RSAPrivateCrtKey rsaPrivateKey = (RSAPrivateCrtKey) privateKey; + key = OpenSSLRSAPrivateCrtKey.getInstance(rsaPrivateKey); + } else if (privateKey instanceof RSAPrivateKey) { + RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey; + key = OpenSSLRSAPrivateKey.getInstance(rsaPrivateKey); + } else { + throw new InvalidKeyException("Need DSA or RSA private key"); + } + + // Allocate buffer according to RSA modulus size. + int maxSize = NativeCrypto.RSA_size(key.getPkeyContext()); + inputBuffer = new byte[maxSize]; + inputOffset = 0; + } + + @Override + protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { + if (publicKey instanceof OpenSSLRSAPublicKey) { + OpenSSLRSAPublicKey rsaPublicKey = (OpenSSLRSAPublicKey) publicKey; + key = rsaPublicKey.getOpenSSLKey(); + } else if (publicKey instanceof RSAPublicKey) { + RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey; + key = OpenSSLRSAPublicKey.getInstance(rsaPublicKey); + } else { + throw new InvalidKeyException("Need DSA or RSA public key"); + } + + // Allocate buffer according to RSA modulus size. + int maxSize = NativeCrypto.RSA_size(key.getPkeyContext()); + inputBuffer = new byte[maxSize]; + inputOffset = 0; + } + + @Override + protected void engineSetParameter(String param, Object value) throws InvalidParameterException { + } + + @Override + protected byte[] engineSign() throws SignatureException { + if (key == null) { + // This can't actually happen, but you never know... + throw new SignatureException("Need RSA private key"); + } + + if (inputIsTooLong) { + throw new SignatureException("input length " + inputOffset + " != " + + inputBuffer.length + " (modulus size)"); + } + + byte[] outputBuffer = new byte[inputBuffer.length]; + try { + NativeCrypto.RSA_private_encrypt(inputOffset, inputBuffer, outputBuffer, + key.getPkeyContext(), NativeCrypto.RSA_PKCS1_PADDING); + return outputBuffer; + } catch (Exception ex) { + throw new SignatureException(ex); + } finally { + inputOffset = 0; + } + } + + @Override + protected boolean engineVerify(byte[] sigBytes) throws SignatureException { + if (key == null) { + // This can't actually happen, but you never know... + throw new SignatureException("Need RSA public key"); + } + + if (inputIsTooLong) { + return false; + } + + byte[] outputBuffer = new byte[inputBuffer.length]; + try { + final int resultSize; + try { + resultSize = NativeCrypto.RSA_public_decrypt(sigBytes.length, sigBytes, + outputBuffer, key.getPkeyContext(), NativeCrypto.RSA_PKCS1_PADDING); + } catch (SignatureException e) { + throw e; + } catch (Exception e) { + return false; + } + /* Make this constant time by comparing every byte. */ + boolean matches = (resultSize == inputOffset); + for (int i = 0; i < resultSize; i++) { + if (inputBuffer[i] != outputBuffer[i]) { + matches = false; + } + } + return matches; + } catch (Exception ex) { + throw new SignatureException(ex); + } finally { + inputOffset = 0; + } + } +} diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java index 4c92952..4cc16e6 100644 --- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java @@ -42,7 +42,11 @@ import javax.net.ssl.SSLProtocolException; import javax.net.ssl.SSLSession; import javax.net.ssl.X509TrustManager; import javax.security.auth.x500.X500Principal; +import static libcore.io.OsConstants.*; +import libcore.io.ErrnoException; +import libcore.io.Libcore; import libcore.io.Streams; +import libcore.io.StructTimeval; import org.apache.harmony.security.provider.cert.X509CertImpl; /** @@ -51,7 +55,6 @@ import org.apache.harmony.security.provider.cert.X509CertImpl; * Extensions to SSLSocket include: * <ul> * <li>handshake timeout - * <li>compression methods * <li>session tickets * <li>Server Name Indication * </ul> @@ -70,7 +73,6 @@ public class OpenSSLSocketImpl private byte[] npnProtocols; private String[] enabledProtocols; private String[] enabledCipherSuites; - private String[] enabledCompressionMethods; private boolean useSessionTickets; private String hostname; private OpenSSLSessionImpl sslSession; @@ -95,7 +97,8 @@ public class OpenSSLSocketImpl * OpenSSLSocketImplWrapper overrides setSoTimeout and * getSoTimeout to delegate to the wrapped socket. */ - private int timeoutMilliseconds = 0; + private int readTimeoutMilliseconds = 0; + private int writeTimeoutMilliseconds = 0; private int handshakeTimeoutMilliseconds = -1; // -1 = same as timeout; 0 = infinite private String wrappedHost; @@ -108,10 +111,9 @@ public class OpenSSLSocketImpl protected OpenSSLSocketImpl(SSLParametersImpl sslParameters, String[] enabledProtocols, - String[] enabledCipherSuites, - String[] enabledCompressionMethods) throws IOException { + String[] enabledCipherSuites) throws IOException { this.socket = this; - init(sslParameters, enabledProtocols, enabledCipherSuites, enabledCompressionMethods); + init(sslParameters, enabledProtocols, enabledCipherSuites); } protected OpenSSLSocketImpl(String host, int port, SSLParametersImpl sslParameters) @@ -169,8 +171,7 @@ public class OpenSSLSocketImpl private void init(SSLParametersImpl sslParameters) throws IOException { init(sslParameters, NativeCrypto.getDefaultProtocols(), - NativeCrypto.getDefaultCipherSuites(), - NativeCrypto.getDefaultCompressionMethods()); + NativeCrypto.getDefaultCipherSuites()); } /** @@ -179,12 +180,10 @@ public class OpenSSLSocketImpl */ private void init(SSLParametersImpl sslParameters, String[] enabledProtocols, - String[] enabledCipherSuites, - String[] enabledCompressionMethods) throws IOException { + String[] enabledCipherSuites) throws IOException { this.sslParameters = sslParameters; this.enabledProtocols = enabledProtocols; this.enabledCipherSuites = enabledCipherSuites; - this.enabledCompressionMethods = enabledCompressionMethods; } /** @@ -225,20 +224,6 @@ public class OpenSSLSocketImpl return null; } - String compressionMethod = session.getCompressionMethod(); - if (!compressionMethod.equals(NativeCrypto.SUPPORTED_COMPRESSION_METHOD_NULL)) { - boolean compressionMethodFound = false; - for (String enabledCompressionMethod : enabledCompressionMethods) { - if (compressionMethod.equals(enabledCompressionMethod)) { - compressionMethodFound = true; - break; - } - } - if (!compressionMethodFound) { - return null; - } - } - return session; } @@ -316,10 +301,6 @@ public class OpenSSLSocketImpl NativeCrypto.setEnabledProtocols(sslNativePointer, enabledProtocols); NativeCrypto.setEnabledCipherSuites(sslNativePointer, enabledCipherSuites); - if (enabledCompressionMethods.length != 0) { - NativeCrypto.setEnabledCompressionMethods(sslNativePointer, - enabledCompressionMethods); - } if (useSessionTickets) { NativeCrypto.SSL_clear_options(sslNativePointer, NativeCrypto.SSL_OP_NO_TICKET); } @@ -385,9 +366,11 @@ public class OpenSSLSocketImpl } // Temporarily use a different timeout for the handshake process - int savedTimeoutMilliseconds = getSoTimeout(); + int savedReadTimeoutMilliseconds = getSoTimeout(); + int savedWriteTimeoutMilliseconds = getSoWriteTimeout(); if (handshakeTimeoutMilliseconds >= 0) { setSoTimeout(handshakeTimeoutMilliseconds); + setSoWriteTimeout(handshakeTimeoutMilliseconds); } int sslSessionNativePointer; @@ -423,7 +406,8 @@ public class OpenSSLSocketImpl // Restore the original timeout now that the handshake is complete if (handshakeTimeoutMilliseconds >= 0) { - setSoTimeout(savedTimeoutMilliseconds); + setSoTimeout(savedReadTimeoutMilliseconds); + setSoWriteTimeout(savedWriteTimeoutMilliseconds); } // if not, notifyHandshakeCompletedListeners later in handshakeCompleted() callback @@ -442,7 +426,7 @@ public class OpenSSLSocketImpl } } - private String getPeerHostName() { + String getPeerHostName() { if (wrappedHost != null) { return wrappedHost; } @@ -453,7 +437,7 @@ public class OpenSSLSocketImpl return null; } - private int getPeerPort() { + int getPeerPort() { return wrappedHost == null ? super.getPort() : wrappedPort; } @@ -594,8 +578,13 @@ public class OpenSSLSocketImpl } boolean client = sslParameters.getUseClientMode(); if (client) { - sslParameters.getTrustManager().checkServerTrusted(peerCertificateChain, - authMethod); + X509TrustManager x509tm = sslParameters.getTrustManager(); + if (x509tm instanceof TrustManagerImpl) { + TrustManagerImpl tm = (TrustManagerImpl) x509tm; + tm.checkServerTrusted(peerCertificateChain, authMethod, wrappedHost); + } else { + x509tm.checkServerTrusted(peerCertificateChain, authMethod); + } } else { String authType = peerCertificateChain[0].getPublicKey().getAlgorithm(); sslParameters.getTrustManager().checkClientTrusted(peerCertificateChain, @@ -715,7 +704,7 @@ public class OpenSSLSocketImpl return; } NativeCrypto.SSL_write(sslNativePointer, socket.getFileDescriptor$(), - OpenSSLSocketImpl.this, buf, offset, byteCount); + OpenSSLSocketImpl.this, buf, offset, byteCount, writeTimeoutMilliseconds); } } } @@ -793,35 +782,6 @@ public class OpenSSLSocketImpl } /** - * The names of the compression methods that may be used on this SSL - * connection. - * @return an array of compression methods - */ - public String[] getSupportedCompressionMethods() { - return NativeCrypto.getSupportedCompressionMethods(); - } - - /** - * The names of the compression methods versions that are in use - * on this SSL connection. - * - * @return an array of compression methods - */ - public String[] getEnabledCompressionMethods() { - return enabledCompressionMethods.clone(); - } - - /** - * Enables compression methods listed by getSupportedCompressionMethods(). - * - * @throws IllegalArgumentException when one or more of the names in the - * array are not supported, or when the array is null. - */ - public void setEnabledCompressionMethods(String[] methods) { - enabledCompressionMethods = NativeCrypto.checkEnabledCompressionMethods(methods); - } - - /** * This method enables session ticket support. * * @param useSessionTickets True to enable session tickets @@ -875,21 +835,42 @@ public class OpenSSLSocketImpl throw new SocketException("Methods sendUrgentData, setOOBInline are not supported."); } - @Override public void setSoTimeout(int timeoutMilliseconds) throws SocketException { - super.setSoTimeout(timeoutMilliseconds); - this.timeoutMilliseconds = timeoutMilliseconds; + @Override public void setSoTimeout(int readTimeoutMilliseconds) throws SocketException { + super.setSoTimeout(readTimeoutMilliseconds); + this.readTimeoutMilliseconds = readTimeoutMilliseconds; } @Override public int getSoTimeout() throws SocketException { - return timeoutMilliseconds; + return readTimeoutMilliseconds; + } + + /** + * Note write timeouts are not part of the javax.net.ssl.SSLSocket API + */ + public void setSoWriteTimeout(int writeTimeoutMilliseconds) throws SocketException { + this.writeTimeoutMilliseconds = writeTimeoutMilliseconds; + + StructTimeval tv = StructTimeval.fromMillis(writeTimeoutMilliseconds); + try { + Libcore.os.setsockoptTimeval(getFileDescriptor$(), SOL_SOCKET, SO_SNDTIMEO, tv); + } catch (ErrnoException errnoException) { + throw errnoException.rethrowAsSocketException(); + } + } + + /** + * Note write timeouts are not part of the javax.net.ssl.SSLSocket API + */ + public int getSoWriteTimeout() throws SocketException { + return writeTimeoutMilliseconds; } /** * Set the handshake timeout on this socket. This timeout is specified in * milliseconds and will be used only during the handshake process. */ - public void setHandshakeTimeout(int timeoutMilliseconds) throws SocketException { - this.handshakeTimeoutMilliseconds = timeoutMilliseconds; + public void setHandshakeTimeout(int handshakeTimeoutMilliseconds) throws SocketException { + this.handshakeTimeoutMilliseconds = handshakeTimeoutMilliseconds; } @Override public void close() throws IOException { @@ -914,12 +895,13 @@ public class OpenSSLSocketImpl } } - NativeCrypto.SSL_interrupt(sslNativePointer); - synchronized (this) { + + // Interrupt any outstanding reads or writes before taking the writeLock and readLock + NativeCrypto.SSL_interrupt(sslNativePointer); + synchronized (writeLock) { synchronized (readLock) { - // Shut down the SSL connection, per se. try { if (handshakeStarted) { diff --git a/luni/src/main/java/java/util/concurrent/locks/UnsafeAccess.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinEntryException.java index 07f64e4..8b74514 100644 --- a/luni/src/main/java/java/util/concurrent/locks/UnsafeAccess.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinEntryException.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,21 +14,16 @@ * limitations under the License. */ -package java.util.concurrent.locks; +package org.apache.harmony.xnet.provider.jsse; -import sun.misc.Unsafe; +// public for testing by CertPinManagerTest +public class PinEntryException extends Exception { -/** - * Easy access to {@link Unsafe} for the rest of this package. - */ -/*package*/ final class UnsafeAccess { - /** non-null; unique instance of {@link Unsafe} */ - /*package*/ static final Unsafe THE_ONE = Unsafe.getUnsafe(); + PinEntryException() { + } - /** - * This class is uninstantiable. - */ - private UnsafeAccess() { - // This space intentionally left blank. + PinEntryException(String msg) { + super(msg); } } + diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinFailureLogger.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinFailureLogger.java new file mode 100644 index 0000000..40b1838 --- /dev/null +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinFailureLogger.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xnet.provider.jsse; + +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.util.List; +import libcore.io.Base64; +import libcore.io.DropBox; + +public class PinFailureLogger { + + private static final long LOG_INTERVAL_NANOS = 1000 * 1000 * 1000 * 60 * 60; + + private static long lastLoggedNanos = 0; + + public static synchronized void log(String cn, boolean chainContainsUserCert, + boolean pinIsEnforcing, + List<X509Certificate> chain) { + // if we've logged recently, don't do it again + if (!timeToLog()) { + return; + } + // otherwise, log the event + writeToLog(cn, chainContainsUserCert, pinIsEnforcing, chain); + // update the last logged time + lastLoggedNanos = System.nanoTime(); + } + + protected static synchronized void writeToLog(String cn, boolean chainContainsUserCert, + boolean pinIsEnforcing, + List<X509Certificate> chain) { + StringBuilder sb = new StringBuilder(); + sb.append(cn); + sb.append("|"); + sb.append(chainContainsUserCert); + sb.append("|"); + sb.append(pinIsEnforcing); + sb.append("|"); + for (X509Certificate cert : chain) { + try { + sb.append(Base64.encode(cert.getEncoded())); + } catch (CertificateEncodingException e) { + sb.append("Error: could not encode certificate"); + } + sb.append("|"); + } + DropBox.addText("cert_pin_failure", sb.toString()); + } + + protected static boolean timeToLog() { + long currentTimeNanos = System.nanoTime(); + return ((currentTimeNanos - lastLoggedNanos) > LOG_INTERVAL_NANOS); + } +} + diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinListEntry.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinListEntry.java new file mode 100644 index 0000000..c05a391 --- /dev/null +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinListEntry.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.xnet.provider.jsse; + +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import libcore.io.EventLogger; + +/** + * This class represents a single entry in the pin file. + */ +// public for testing by CertPinManagerTest +public class PinListEntry { + + /** The Common Name (CN) as used on the SSL certificate */ + private final String cn; + + /** + * Determines whether a failed match here will prevent the chain from being accepted. If true, + * an unpinned chain will log and cause a match failure. If false, it will merely log. + */ + private final boolean enforcing; + + private final Set<String> pinnedFingerprints = new HashSet<String>(); + + private static final boolean DEBUG = false; + + private final TrustedCertificateStore certStore; + + public String getCommonName() { + return cn; + } + + public boolean getEnforcing() { + return enforcing; + } + + public PinListEntry(String entry, TrustedCertificateStore store) throws PinEntryException { + if (entry == null) { + throw new NullPointerException("entry == null"); + } + certStore = store; + // Examples: + // *.google.com=true|34c8a0d...9e04ca05f,9e04ca05f...34c8a0d + // *.android.com=true|ca05f...8a0d34c + // clients.google.com=false|9e04ca05f...34c8a0d,34c8a0d...9e04ca05f + String[] values = entry.split("[=,|]"); + // entry must have a CN, an enforcement value, and at least one pin + if (values.length < 3) { + throw new PinEntryException("Received malformed pin entry"); + } + // get the cn + cn = values[0]; // is there more validation we can do here? + enforcing = enforcementValueFromString(values[1]); + // the remainder should be pins + addPins(Arrays.copyOfRange(values, 2, values.length)); + } + + private static boolean enforcementValueFromString(String val) throws PinEntryException { + if (val.equals("true")) { + return true; + } else if (val.equals("false")) { + return false; + } else { + throw new PinEntryException("Enforcement status is not a valid value"); + } + } + + /** + * Checks the given chain against the pin list corresponding to this entry. + * + * If the pin list does not contain the required certs and the enforcing field is true then + * this returns true, indicating a verification error. Otherwise, it returns false and + * verification should proceed. + */ + public boolean chainIsNotPinned(List<X509Certificate> chain) { + for (X509Certificate cert : chain) { + String fingerprint = getFingerprint(cert); + if (pinnedFingerprints.contains(fingerprint)) { + return false; + } + } + logPinFailure(chain); + return enforcing; + } + + private static String getFingerprint(X509Certificate cert) { + try { + MessageDigest dgst = MessageDigest.getInstance("SHA512"); + byte[] encoded = cert.getPublicKey().getEncoded(); + byte[] fingerprint = dgst.digest(encoded); + return IntegralToString.bytesToHexString(fingerprint, false); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + private void addPins(String[] pins) { + for (String pin : pins) { + validatePin(pin); + } + Collections.addAll(pinnedFingerprints, pins); + } + + private static void validatePin(String pin) { + // check to make sure the length is correct + if (pin.length() != 128) { + throw new IllegalArgumentException("Pin is not a valid length"); + } + // check to make sure that it's a valid hex string + try { + new BigInteger(pin, 16); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Pin is not a valid hex string", e); + } + } + + private boolean chainContainsUserCert(List<X509Certificate> chain) { + if (certStore == null) { + return false; + } + for (X509Certificate cert : chain) { + if (certStore.isUserAddedCertificate(cert)) { + return true; + } + } + return false; + } + + private void logPinFailure(List<X509Certificate> chain) { + PinFailureLogger.log(cn, chainContainsUserCert(chain), enforcing, chain); + } +} + diff --git a/luni/src/main/java/java/util/concurrent/atomic/UnsafeAccess.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinManagerException.java index 96fff17..74b3c65 100644 --- a/luni/src/main/java/java/util/concurrent/atomic/UnsafeAccess.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/PinManagerException.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,21 +14,19 @@ * limitations under the License. */ -package java.util.concurrent.atomic; +package org.apache.harmony.xnet.provider.jsse; -import sun.misc.Unsafe; +class PinManagerException extends Exception { -/** - * Easy access to {@link Unsafe} for the rest of this package. - */ -/*package*/ final class UnsafeAccess { - /** non-null; unique instance of {@link Unsafe} */ - /*package*/ static final Unsafe THE_ONE = Unsafe.getUnsafe(); + PinManagerException() { + } - /** - * This class is uninstantiable. - */ - private UnsafeAccess() { - // This space intentionally left blank. + PinManagerException(String msg) { + super(msg); + } + + PinManagerException(String msg, Exception e) { + super(msg, e); } } + diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketFactoryImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketFactoryImpl.java index be9a7fc..93496cf 100644 --- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketFactoryImpl.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketFactoryImpl.java @@ -88,7 +88,7 @@ public class SSLSocketFactoryImpl extends SSLSocketFactory { if (instantiationException != null) { throw instantiationException; } - return new SSLSocketWrapper(s, autoClose, (SSLParametersImpl) sslParameters + return new SSLSocketWrapper(s, host, port, autoClose, (SSLParametersImpl) sslParameters .clone()); } diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketImpl.java index 6e5fddd..2cd2cf5 100644 --- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketImpl.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketImpl.java @@ -41,6 +41,10 @@ public class SSLSocketImpl extends SSLSocket { // indicates if handshake has been started private boolean handshake_started = false; + // used when we're wrapping a socket + private final String wrappedHost; + private final int wrappedPort; + // record protocol to be used protected SSLRecordProtocol recordProtocol; // handshake protocol to be used @@ -83,6 +87,8 @@ public class SSLSocketImpl extends SSLSocket { */ protected SSLSocketImpl(SSLParametersImpl sslParameters) { this.sslParameters = sslParameters; + this.wrappedHost = null; + this.wrappedPort = -1; // init should be called after creation! } @@ -99,6 +105,8 @@ public class SSLSocketImpl extends SSLSocket { protected SSLSocketImpl(String host, int port, SSLParametersImpl sslParameters) throws IOException, UnknownHostException { super(host, port); + this.wrappedHost = host; + this.wrappedPort = port; this.sslParameters = sslParameters; init(); } @@ -120,6 +128,8 @@ public class SSLSocketImpl extends SSLSocket { SSLParametersImpl sslParameters) throws IOException, UnknownHostException { super(host, port, localHost, localPort); + this.wrappedHost = host; + this.wrappedPort = port; this.sslParameters = sslParameters; init(); } @@ -138,6 +148,8 @@ public class SSLSocketImpl extends SSLSocket { SSLParametersImpl sslParameters) throws IOException { super(host, port); this.sslParameters = sslParameters; + this.wrappedHost = null; + this.wrappedPort = -1; init(); } @@ -158,6 +170,8 @@ public class SSLSocketImpl extends SSLSocket { SSLParametersImpl sslParameters) throws IOException { super(address, port, localAddress, localPort); this.sslParameters = sslParameters; + this.wrappedHost = null; + this.wrappedPort = -1; init(); } @@ -193,6 +207,29 @@ public class SSLSocketImpl extends SSLSocket { } } + String getWrappedHostName() { + return wrappedHost; + } + + int getWrappedPort() { + return wrappedPort; + } + + String getPeerHostName() { + if (wrappedHost != null) { + return wrappedHost; + } + InetAddress inetAddress = super.getInetAddress(); + if (inetAddress != null) { + return inetAddress.getHostName(); + } + return null; + } + + int getPeerPort() { + return (wrappedPort == -1) ? super.getPort() : wrappedPort; + } + // --------------- SSLParameters based methods --------------------- /** diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketWrapper.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketWrapper.java index 27bbead..a393e24 100644 --- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketWrapper.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketWrapper.java @@ -32,8 +32,9 @@ public class SSLSocketWrapper extends SSLSocketImpl { private final Socket socket; private final boolean autoClose; - protected SSLSocketWrapper(Socket socket, boolean autoClose, SSLParametersImpl sslParameters) throws IOException { - super(sslParameters); + protected SSLSocketWrapper(Socket socket, String host, int port, boolean autoClose, + SSLParametersImpl sslParameters) throws IOException { + super(host, port, sslParameters); if (!socket.isConnected()) { throw new SocketException("Socket is not connected."); } diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHandshakeImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHandshakeImpl.java index c5e1838..fa8d291 100644 --- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHandshakeImpl.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHandshakeImpl.java @@ -19,7 +19,6 @@ package org.apache.harmony.xnet.provider.jsse; import java.io.IOException; import java.math.BigInteger; -import java.security.AccessController; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; @@ -659,7 +658,7 @@ public class ServerHandshakeImpl extends HandshakeProtocol { } else { if ((parameters.getNeedClientAuth() && clientCert == null) || clientKeyExchange == null - || (clientCert != null + || (clientCert != null && clientCert.certs.length > 0 && !clientKeyExchange.isEmpty() && certificateVerify == null)) { unexpectedMessage(); diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java index 3f362c5..0218249 100644 --- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java @@ -35,6 +35,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import javax.net.ssl.X509TrustManager; +import libcore.io.EventLogger; /** * @@ -52,6 +53,11 @@ public final class TrustManagerImpl implements X509TrustManager { private final KeyStore rootKeyStore; /** + * The CertPinManager, which validates the chain against a host-to-pin mapping + */ + private CertPinManager pinManager; + + /** * The backing store for the AndroidCAStore if non-null. This will * be null when the rootKeyStore is null, implying we are not * using the AndroidCAStore. @@ -83,6 +89,13 @@ public final class TrustManagerImpl implements X509TrustManager { * @param ks */ public TrustManagerImpl(KeyStore keyStore) { + this(keyStore, null); + } + + /** + * For testing only + */ + public TrustManagerImpl(KeyStore keyStore, CertPinManager manager) { CertPathValidator validatorLocal = null; CertificateFactory factoryLocal = null; KeyStore rootKeyStoreLocal = null; @@ -111,6 +124,17 @@ public final class TrustManagerImpl implements X509TrustManager { } catch (Exception e) { errLocal = e; } + + if (manager != null) { + this.pinManager = manager; + } else { + try { + pinManager = new CertPinManager(trustedCertificateStoreLocal); + } catch (PinManagerException e) { + throw new SecurityException("Could not initialize CertPinManager", e); + } + } + this.rootKeyStore = rootKeyStoreLocal; this.trustedCertificateStore = trustedCertificateStoreLocal; this.validator = validatorLocal; @@ -155,12 +179,22 @@ public final class TrustManagerImpl implements X509TrustManager { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { - checkTrusted(chain, authType); + checkTrusted(chain, authType, null); } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { - checkTrusted(chain, authType); + checkTrusted(chain, authType, null); + } + + /** + * Validates whether a server is trusted. If hostname is given and non-null it also checks if + * chain is pinned appropriately for that host. If null, it does not check for pinned certs. + * The return value is a list of the certificates used for making the trust decision. + */ + public List<X509Certificate> checkServerTrusted(X509Certificate[] chain, String authType, + String host) throws CertificateException { + return checkTrusted(chain, authType, host); } public void handleTrustStorageUpdate() { @@ -168,10 +202,10 @@ public final class TrustManagerImpl implements X509TrustManager { trustedCertificateIndex.reset(); } else { trustedCertificateIndex.reset(trustAnchors(acceptedIssuers)); - } + } } - private void checkTrusted(X509Certificate[] chain, String authType) + private List<X509Certificate> checkTrusted(X509Certificate[] chain, String authType, String host) throws CertificateException { if (chain == null || chain.length == 0 || authType == null || authType.length() == 0) { throw new IllegalArgumentException("null or zero-length parameter"); @@ -180,21 +214,71 @@ public final class TrustManagerImpl implements X509TrustManager { throw new CertificateException(err); } - Set<TrustAnchor> trustAnchors = new HashSet<TrustAnchor>(); - X509Certificate[] newChain = cleanupCertChainAndFindTrustAnchors(chain, trustAnchors); + // get the cleaned up chain and trust anchor + Set<TrustAnchor> trustAnchor = new HashSet<TrustAnchor>(); // there can only be one! + X509Certificate[] newChain = cleanupCertChainAndFindTrustAnchors(chain, trustAnchor); + + // add the first trust anchor to the chain, which may be an intermediate + List<X509Certificate> wholeChain = new ArrayList<X509Certificate>(); + wholeChain.addAll(Arrays.asList(newChain)); + // trustAnchor is actually just a single element + for (TrustAnchor trust : trustAnchor) { + wholeChain.add(trust.getTrustedCert()); + } + + // add all the cached certificates from the cert index, avoiding loops + // this gives us a full chain from leaf to root, which we use for cert pinning and pass + // back out to callers when we return. + X509Certificate last = wholeChain.get(wholeChain.size() - 1); + while (true) { + TrustAnchor cachedTrust = trustedCertificateIndex.findByIssuerAndSignature(last); + // the cachedTrust can be null if there isn't anything in the index or if a user has + // trusted a non-self-signed cert. + if (cachedTrust == null) { + break; + } + + // at this point we have a cached trust anchor, but don't know if its one we got from + // the server. Extract the cert, compare it to the last element in the chain, and add it + // if we haven't seen it before. + X509Certificate next = cachedTrust.getTrustedCert(); + if (next != last) { + wholeChain.add(next); + last = next; + } else { + // if next == last then we found a self-signed cert and the chain is done + break; + } + } + + // build the cert path from the array of certs sans trust anchors + CertPath certPath = factory.generateCertPath(Arrays.asList(newChain)); + + if (host != null) { + boolean chainIsNotPinned = true; + try { + chainIsNotPinned = pinManager.chainIsNotPinned(host, wholeChain); + } catch (PinManagerException e) { + throw new CertificateException(e); + } + if (chainIsNotPinned) { + throw new CertificateException(new CertPathValidatorException( + "Certificate path is not properly pinned.", null, certPath, -1)); + } + } + if (newChain.length == 0) { // chain was entirely trusted, skip the validator - return; + return wholeChain; } - CertPath certPath = factory.generateCertPath(Arrays.asList(newChain)); - if (trustAnchors.isEmpty()) { + if (trustAnchor.isEmpty()) { throw new CertificateException(new CertPathValidatorException( "Trust anchor for certification path not found.", null, certPath, -1)); } try { - PKIXParameters params = new PKIXParameters(trustAnchors); + PKIXParameters params = new PKIXParameters(trustAnchor); params.setRevocationEnabled(false); validator.validate(certPath, params); // Add intermediate CAs to the index to tolerate sites @@ -211,6 +295,8 @@ public final class TrustManagerImpl implements X509TrustManager { } catch (CertPathValidatorException e) { throw new CertificateException(e); } + + return wholeChain; } /** @@ -232,17 +318,9 @@ public final class TrustManagerImpl implements X509TrustManager { // Start with the first certificate in the chain, assuming it // is the leaf certificate (server or client cert). for (currIndex = 0; currIndex < chain.length; currIndex++) { - // If the current cert is a TrustAnchor, we can ignore the rest of the chain. - // This avoids including "bridge" CA certs that added for legacy compatability. - TrustAnchor trustAnchor = findTrustAnchorBySubjectAndPublicKey(chain[currIndex]); - if (trustAnchor != null) { - trustAnchors.add(trustAnchor); - currIndex--; - break; - } - // Walk the rest of the chain to find a "subject" matching + // Walk the chain to find a "subject" matching // the "issuer" of the current certificate. In a properly - // order chain this should be the next cert and be fast. + // ordered chain this should be the next cert and be fast. // If not, we reorder things to be as the validator will // expect. boolean foundNext = false; @@ -271,15 +349,27 @@ public final class TrustManagerImpl implements X509TrustManager { } } - // 2. If the chain is now shorter, copy to an appropriately sized array. - int chainLength = currIndex + 1; + // 2. Find the trust anchor in the chain, if any + int anchorIndex; + for (anchorIndex = 0; anchorIndex < chain.length; anchorIndex++) { + // If the current cert is a TrustAnchor, we can ignore the rest of the chain. + // This avoids including "bridge" CA certs that added for legacy compatibility. + TrustAnchor trustAnchor = findTrustAnchorBySubjectAndPublicKey(chain[anchorIndex]); + if (trustAnchor != null) { + trustAnchors.add(trustAnchor); + break; + } + } + + // 3. If the chain is now shorter, copy to an appropriately sized array. + int chainLength = anchorIndex; X509Certificate[] newChain = ((chainLength == chain.length) ? chain : Arrays.copyOf(chain, chainLength)); - // 3. If no TrustAnchor was found in cleanup, look for one now + // 4. If we didn't find a trust anchor earlier, look for one now if (trustAnchors.isEmpty()) { - TrustAnchor trustAnchor = findTrustAnchorByIssuerAndSignature(newChain[chainLength-1]); + TrustAnchor trustAnchor = findTrustAnchorByIssuerAndSignature(newChain[anchorIndex-1]); if (trustAnchor != null) { trustAnchors.add(trustAnchor); } diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustedCertificateStore.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustedCertificateStore.java index 54116a7..e7b1a7c 100644 --- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustedCertificateStore.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustedCertificateStore.java @@ -16,6 +16,11 @@ package org.apache.harmony.xnet.provider.jsse; +import org.apache.harmony.security.x501.Name; +import org.apache.harmony.security.x509.AuthorityKeyIdentifier; +import org.apache.harmony.security.x509.GeneralName; +import org.apache.harmony.security.x509.GeneralNames; +import org.apache.harmony.security.x509.SubjectKeyIdentifier; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; @@ -23,19 +28,20 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.security.KeyStoreSpi; -import java.security.PublicKey; -import java.security.cert.CertSelector; +import java.math.BigInteger; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; -import java.util.Collections; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.HashSet; +import java.util.List; import java.util.Set; import javax.security.auth.x500.X500Principal; import libcore.io.IoUtils; +import libcore.util.Objects; /** * A source for trusted root certificate authority (CA) certificates @@ -292,6 +298,14 @@ public final class TrustedCertificateStore { } /** + * Returns true to indicate that the certificate was added by the + * user, false otherwise. + */ + public boolean isUserAddedCertificate(X509Certificate cert) { + return getCertificateFile(addedDir, cert).exists(); + } + + /** * Returns a File for where the certificate is found if it exists * or where it should be installed if it does not exist. The * caller can disambiguate these cases by calling {@code @@ -366,6 +380,109 @@ public final class TrustedCertificateStore { return null; } + private static AuthorityKeyIdentifier getAuthorityKeyIdentifier(X509Certificate cert) { + final byte[] akidBytes = cert.getExtensionValue("2.5.29.35"); + if (akidBytes == null) { + return null; + } + + try { + return AuthorityKeyIdentifier.decode(akidBytes); + } catch (IOException e) { + return null; + } + } + + private static SubjectKeyIdentifier getSubjectKeyIdentifier(X509Certificate cert) { + final byte[] skidBytes = cert.getExtensionValue("2.5.29.14"); + if (skidBytes == null) { + return null; + } + + try { + return SubjectKeyIdentifier.decode(skidBytes); + } catch (IOException e) { + return null; + } + } + + private static boolean isSelfSignedCertificate(X509Certificate cert) { + if (!Objects.equal(cert.getSubjectX500Principal(), cert.getIssuerX500Principal())) { + return false; + } + + final AuthorityKeyIdentifier akid = getAuthorityKeyIdentifier(cert); + if (akid != null) { + final byte[] akidKeyId = akid.getKeyIdentifier(); + if (akidKeyId != null) { + final SubjectKeyIdentifier skid = getSubjectKeyIdentifier(cert); + if (!Arrays.equals(akidKeyId, skid.getKeyIdentifier())) { + return false; + } + } + + final BigInteger akidSerial = akid.getAuthorityCertSerialNumber(); + if (akidSerial != null && !akidSerial.equals(cert.getSerialNumber())) { + return false; + } + + final GeneralNames possibleIssuerNames = akid.getAuthorityCertIssuer(); + if (possibleIssuerNames != null) { + GeneralName issuerName = null; + + /* Get the first Directory Name (DN) to match how OpenSSL works. */ + for (GeneralName possibleName : possibleIssuerNames.getNames()) { + if (possibleName.getTag() == GeneralName.DIR_NAME) { + issuerName = possibleName; + break; + } + } + + if (issuerName != null) { + final String issuerCanonical = ((Name) issuerName.getName()) + .getName(X500Principal.CANONICAL); + + try { + final String subjectCanonical = new Name(cert.getSubjectX500Principal() + .getEncoded()).getName(X500Principal.CANONICAL); + if (!issuerCanonical.equals(subjectCanonical)) { + return false; + } + } catch (IOException ignored) { + } + } + } + } + + return true; + } + + /** + * Attempt to build a certificate chain from the supplied {@code leaf} + * argument through the chain of issuers as high up as known. If the chain + * can't be completed, the most complete chain available will be returned. + * This means that a list with only the {@code leaf} certificate is returned + * if no issuer certificates could be found. + */ + public List<X509Certificate> getCertificateChain(X509Certificate leaf) { + final List<X509Certificate> chain = new ArrayList<X509Certificate>(); + chain.add(leaf); + + for (int i = 0; true; i++) { + X509Certificate cert = chain.get(i); + if (isSelfSignedCertificate(cert)) { + break; + } + X509Certificate issuer = findIssuer(cert); + if (issuer == null) { + break; + } + chain.add(issuer); + } + + return chain; + } + // like java.security.cert.CertSelector but with X509Certificate and without cloning private static interface CertSelector { public boolean match(X509Certificate cert); |