mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
8284161: Implementation of Virtual Threads (Preview)
Co-authored-by: Ron Pressler <rpressler@openjdk.org> Co-authored-by: Alan Bateman <alanb@openjdk.org> Co-authored-by: Erik Österlund <eosterlund@openjdk.org> Co-authored-by: Andrew Haley <aph@openjdk.org> Co-authored-by: Rickard Bäckman <rbackman@openjdk.org> Co-authored-by: Markus Grönlund <mgronlun@openjdk.org> Co-authored-by: Leonid Mesnik <lmesnik@openjdk.org> Co-authored-by: Serguei Spitsyn <sspitsyn@openjdk.org> Co-authored-by: Chris Plummer <cjplummer@openjdk.org> Co-authored-by: Coleen Phillimore <coleenp@openjdk.org> Co-authored-by: Robbin Ehn <rehn@openjdk.org> Co-authored-by: Stefan Karlsson <stefank@openjdk.org> Co-authored-by: Thomas Schatzl <tschatzl@openjdk.org> Co-authored-by: Sergey Kuksenko <skuksenko@openjdk.org> Reviewed-by: lancea, eosterlund, rehn, sspitsyn, stefank, tschatzl, dfuchs, lmesnik, dcubed, kevinw, amenkov, dlong, mchung, psandoz, bpb, coleenp, smarks, egahlin, mseledtsov, coffeys, darcy
This commit is contained in:
parent
5212535a27
commit
9583e3657e
1133 changed files with 95935 additions and 8335 deletions
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1994, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1994, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -25,6 +25,7 @@
|
|||
|
||||
package java.io;
|
||||
|
||||
import jdk.internal.misc.InternalLock;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.util.ArraysSupport;
|
||||
|
||||
|
@ -63,6 +64,9 @@ public class BufferedInputStream extends FilterInputStream {
|
|||
private static final long BUF_OFFSET
|
||||
= U.objectFieldOffset(BufferedInputStream.class, "buf");
|
||||
|
||||
// initialized to null when BufferedInputStream is sub-classed
|
||||
private final InternalLock lock;
|
||||
|
||||
/**
|
||||
* The internal buffer array where the data is stored. When necessary,
|
||||
* it may be replaced by another array of
|
||||
|
@ -199,12 +203,19 @@ public class BufferedInputStream extends FilterInputStream {
|
|||
throw new IllegalArgumentException("Buffer size <= 0");
|
||||
}
|
||||
buf = new byte[size];
|
||||
|
||||
// use monitors when BufferedInputStream is sub-classed
|
||||
if (getClass() == BufferedInputStream.class) {
|
||||
lock = InternalLock.newLockOrNull();
|
||||
} else {
|
||||
lock = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the buffer with more data, taking into account
|
||||
* shuffling and other tricks for dealing with marks.
|
||||
* Assumes that it is being called by a synchronized method.
|
||||
* Assumes that it is being called by a locked method.
|
||||
* This method also assumes that all data has already been read in,
|
||||
* hence pos > count.
|
||||
*/
|
||||
|
@ -258,7 +269,22 @@ public class BufferedInputStream extends FilterInputStream {
|
|||
* or an I/O error occurs.
|
||||
* @see java.io.FilterInputStream#in
|
||||
*/
|
||||
public synchronized int read() throws IOException {
|
||||
public int read() throws IOException {
|
||||
if (lock != null) {
|
||||
lock.lock();
|
||||
try {
|
||||
return implRead();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (this) {
|
||||
return implRead();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int implRead() throws IOException {
|
||||
if (pos >= count) {
|
||||
fill();
|
||||
if (pos >= count)
|
||||
|
@ -328,9 +354,22 @@ public class BufferedInputStream extends FilterInputStream {
|
|||
* invoking its {@link #close()} method,
|
||||
* or an I/O error occurs.
|
||||
*/
|
||||
public synchronized int read(byte[] b, int off, int len)
|
||||
throws IOException
|
||||
{
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
if (lock != null) {
|
||||
lock.lock();
|
||||
try {
|
||||
return implRead(b, off, len);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (this) {
|
||||
return implRead(b, off, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int implRead(byte[] b, int off, int len) throws IOException {
|
||||
getBufIfOpen(); // Check for closed stream
|
||||
if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
|
@ -362,7 +401,22 @@ public class BufferedInputStream extends FilterInputStream {
|
|||
* {@code in.skip(n)} throws an IOException,
|
||||
* or an I/O error occurs.
|
||||
*/
|
||||
public synchronized long skip(long n) throws IOException {
|
||||
public long skip(long n) throws IOException {
|
||||
if (lock != null) {
|
||||
lock.lock();
|
||||
try {
|
||||
return implSkip(n);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (this) {
|
||||
return implSkip(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private long implSkip(long n) throws IOException {
|
||||
getBufIfOpen(); // Check for closed stream
|
||||
if (n <= 0) {
|
||||
return 0;
|
||||
|
@ -403,7 +457,22 @@ public class BufferedInputStream extends FilterInputStream {
|
|||
* invoking its {@link #close()} method,
|
||||
* or an I/O error occurs.
|
||||
*/
|
||||
public synchronized int available() throws IOException {
|
||||
public int available() throws IOException {
|
||||
if (lock != null) {
|
||||
lock.lock();
|
||||
try {
|
||||
return implAvailable();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (this) {
|
||||
return implAvailable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int implAvailable() throws IOException {
|
||||
int n = count - pos;
|
||||
int avail = getInIfOpen().available();
|
||||
return n > (Integer.MAX_VALUE - avail)
|
||||
|
@ -419,7 +488,22 @@ public class BufferedInputStream extends FilterInputStream {
|
|||
* the mark position becomes invalid.
|
||||
* @see java.io.BufferedInputStream#reset()
|
||||
*/
|
||||
public synchronized void mark(int readlimit) {
|
||||
public void mark(int readlimit) {
|
||||
if (lock != null) {
|
||||
lock.lock();
|
||||
try {
|
||||
implMark(readlimit);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (this) {
|
||||
implMark(readlimit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void implMark(int readlimit) {
|
||||
marklimit = readlimit;
|
||||
markpos = pos;
|
||||
}
|
||||
|
@ -440,7 +524,22 @@ public class BufferedInputStream extends FilterInputStream {
|
|||
* method, or an I/O error occurs.
|
||||
* @see java.io.BufferedInputStream#mark(int)
|
||||
*/
|
||||
public synchronized void reset() throws IOException {
|
||||
public void reset() throws IOException {
|
||||
if (lock != null) {
|
||||
lock.lock();
|
||||
try {
|
||||
implReset();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (this) {
|
||||
implReset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void implReset() throws IOException {
|
||||
getBufIfOpen(); // Cause exception if closed
|
||||
if (markpos < 0)
|
||||
throw new IOException("Resetting to invalid mark");
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1994, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -25,6 +25,10 @@
|
|||
|
||||
package java.io;
|
||||
|
||||
import java.util.Arrays;
|
||||
import jdk.internal.misc.InternalLock;
|
||||
import jdk.internal.misc.VM;
|
||||
|
||||
/**
|
||||
* The class implements a buffered output stream. By setting up such
|
||||
* an output stream, an application can write bytes to the underlying
|
||||
|
@ -35,6 +39,12 @@ package java.io;
|
|||
* @since 1.0
|
||||
*/
|
||||
public class BufferedOutputStream extends FilterOutputStream {
|
||||
private static final int DEFAULT_INITIAL_BUFFER_SIZE = 512;
|
||||
private static final int DEFAULT_MAX_BUFFER_SIZE = 8192;
|
||||
|
||||
// initialized to null when BufferedOutputStream is sub-classed
|
||||
private final InternalLock lock;
|
||||
|
||||
/**
|
||||
* The internal buffer where data is stored.
|
||||
*/
|
||||
|
@ -48,6 +58,44 @@ public class BufferedOutputStream extends FilterOutputStream {
|
|||
*/
|
||||
protected int count;
|
||||
|
||||
/**
|
||||
* Max size of the internal buffer.
|
||||
*/
|
||||
private final int maxBufSize;
|
||||
|
||||
/**
|
||||
* Returns the buffer size to use when no output buffer size specified.
|
||||
*/
|
||||
private static int initialBufferSize() {
|
||||
if (VM.isBooted() && Thread.currentThread().isVirtual()) {
|
||||
return DEFAULT_INITIAL_BUFFER_SIZE;
|
||||
} else {
|
||||
return DEFAULT_MAX_BUFFER_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new buffered output stream.
|
||||
*/
|
||||
private BufferedOutputStream(OutputStream out, int initialSize, int maxSize) {
|
||||
super(out);
|
||||
|
||||
if (initialSize <= 0) {
|
||||
throw new IllegalArgumentException("Buffer size <= 0");
|
||||
}
|
||||
|
||||
if (getClass() == BufferedOutputStream.class) {
|
||||
// use InternalLock and resizable buffer when not sub-classed
|
||||
this.lock = InternalLock.newLockOrNull();
|
||||
this.buf = new byte[initialSize]; // resizable
|
||||
} else {
|
||||
// use monitors and no resizing when sub-classed
|
||||
this.lock = null;
|
||||
this.buf = new byte[maxSize];
|
||||
}
|
||||
this.maxBufSize = maxSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new buffered output stream to write data to the
|
||||
* specified underlying output stream.
|
||||
|
@ -55,7 +103,7 @@ public class BufferedOutputStream extends FilterOutputStream {
|
|||
* @param out the underlying output stream.
|
||||
*/
|
||||
public BufferedOutputStream(OutputStream out) {
|
||||
this(out, 8192);
|
||||
this(out, initialBufferSize(), DEFAULT_MAX_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -68,11 +116,7 @@ public class BufferedOutputStream extends FilterOutputStream {
|
|||
* @throws IllegalArgumentException if size <= 0.
|
||||
*/
|
||||
public BufferedOutputStream(OutputStream out, int size) {
|
||||
super(out);
|
||||
if (size <= 0) {
|
||||
throw new IllegalArgumentException("Buffer size <= 0");
|
||||
}
|
||||
buf = new byte[size];
|
||||
this(out, size, size);
|
||||
}
|
||||
|
||||
/** Flush the internal buffer */
|
||||
|
@ -83,6 +127,24 @@ public class BufferedOutputStream extends FilterOutputStream {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Grow buf to fit an additional len bytes if needed.
|
||||
* If possible, it grows by len+1 to avoid flushing when len bytes
|
||||
* are added. A no-op if the buffer is not resizable.
|
||||
*
|
||||
* This method should only be called while holding the lock.
|
||||
*/
|
||||
private void growIfNeeded(int len) {
|
||||
int neededSize = count + len + 1;
|
||||
if (neededSize < 0)
|
||||
neededSize = Integer.MAX_VALUE;
|
||||
int bufSize = buf.length;
|
||||
if (neededSize > bufSize && bufSize < maxBufSize) {
|
||||
int newSize = Math.min(neededSize, maxBufSize);
|
||||
buf = Arrays.copyOf(buf, newSize);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the specified byte to this buffered output stream.
|
||||
*
|
||||
|
@ -90,7 +152,23 @@ public class BufferedOutputStream extends FilterOutputStream {
|
|||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void write(int b) throws IOException {
|
||||
public void write(int b) throws IOException {
|
||||
if (lock != null) {
|
||||
lock.lock();
|
||||
try {
|
||||
implWrite(b);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (this) {
|
||||
implWrite(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void implWrite(int b) throws IOException {
|
||||
growIfNeeded(1);
|
||||
if (count >= buf.length) {
|
||||
flushBuffer();
|
||||
}
|
||||
|
@ -114,15 +192,31 @@ public class BufferedOutputStream extends FilterOutputStream {
|
|||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void write(byte[] b, int off, int len) throws IOException {
|
||||
if (len >= buf.length) {
|
||||
/* If the request length exceeds the size of the output buffer,
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
if (lock != null) {
|
||||
lock.lock();
|
||||
try {
|
||||
implWrite(b, off, len);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (this) {
|
||||
implWrite(b, off, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void implWrite(byte[] b, int off, int len) throws IOException {
|
||||
if (len >= maxBufSize) {
|
||||
/* If the request length exceeds the max size of the output buffer,
|
||||
flush the output buffer and then write the data directly.
|
||||
In this way buffered streams will cascade harmlessly. */
|
||||
flushBuffer();
|
||||
out.write(b, off, len);
|
||||
return;
|
||||
}
|
||||
growIfNeeded(len);
|
||||
if (len > buf.length - count) {
|
||||
flushBuffer();
|
||||
}
|
||||
|
@ -138,7 +232,22 @@ public class BufferedOutputStream extends FilterOutputStream {
|
|||
* @see java.io.FilterOutputStream#out
|
||||
*/
|
||||
@Override
|
||||
public synchronized void flush() throws IOException {
|
||||
public void flush() throws IOException {
|
||||
if (lock != null) {
|
||||
lock.lock();
|
||||
try {
|
||||
implFlush();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (this) {
|
||||
implFlush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void implFlush() throws IOException {
|
||||
flushBuffer();
|
||||
out.flush();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -25,7 +25,6 @@
|
|||
|
||||
package java.io;
|
||||
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
|
@ -33,6 +32,7 @@ import java.util.Spliterator;
|
|||
import java.util.Spliterators;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
import jdk.internal.misc.InternalLock;
|
||||
|
||||
/**
|
||||
* Reads text from a character-input stream, buffering characters so as to
|
||||
|
@ -69,7 +69,6 @@ import java.util.stream.StreamSupport;
|
|||
*/
|
||||
|
||||
public class BufferedReader extends Reader {
|
||||
|
||||
private Reader in;
|
||||
|
||||
private char[] cb;
|
||||
|
@ -176,23 +175,37 @@ public class BufferedReader extends Reader {
|
|||
* @throws IOException If an I/O error occurs
|
||||
*/
|
||||
public int read() throws IOException {
|
||||
synchronized (lock) {
|
||||
ensureOpen();
|
||||
for (;;) {
|
||||
if (nextChar >= nChars) {
|
||||
fill();
|
||||
if (nextChar >= nChars)
|
||||
return -1;
|
||||
}
|
||||
if (skipLF) {
|
||||
skipLF = false;
|
||||
if (cb[nextChar] == '\n') {
|
||||
nextChar++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return cb[nextChar++];
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
return implRead();
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
return implRead();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int implRead() throws IOException {
|
||||
ensureOpen();
|
||||
for (;;) {
|
||||
if (nextChar >= nChars) {
|
||||
fill();
|
||||
if (nextChar >= nChars)
|
||||
return -1;
|
||||
}
|
||||
if (skipLF) {
|
||||
skipLF = false;
|
||||
if (cb[nextChar] == '\n') {
|
||||
nextChar++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return cb[nextChar++];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -277,24 +290,38 @@ public class BufferedReader extends Reader {
|
|||
* @throws IOException {@inheritDoc}
|
||||
*/
|
||||
public int read(char[] cbuf, int off, int len) throws IOException {
|
||||
synchronized (lock) {
|
||||
ensureOpen();
|
||||
Objects.checkFromIndexSize(off, len, cbuf.length);
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
return implRead(cbuf, off, len);
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
|
||||
int n = read1(cbuf, off, len);
|
||||
if (n <= 0) return n;
|
||||
while ((n < len) && in.ready()) {
|
||||
int n1 = read1(cbuf, off + n, len - n);
|
||||
if (n1 <= 0) break;
|
||||
n += n1;
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
return implRead(cbuf, off, len);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
private int implRead(char[] cbuf, int off, int len) throws IOException {
|
||||
ensureOpen();
|
||||
Objects.checkFromIndexSize(off, len, cbuf.length);
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int n = read1(cbuf, off, len);
|
||||
if (n <= 0) return n;
|
||||
while ((n < len) && in.ready()) {
|
||||
int n1 = read1(cbuf, off + n, len - n);
|
||||
if (n1 <= 0) break;
|
||||
n += n1;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a line of text. A line is considered to be terminated by any one
|
||||
* of a line feed ('\n'), a carriage return ('\r'), a carriage return
|
||||
|
@ -314,67 +341,81 @@ public class BufferedReader extends Reader {
|
|||
* @throws IOException If an I/O error occurs
|
||||
*/
|
||||
String readLine(boolean ignoreLF, boolean[] term) throws IOException {
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
return implReadLine(ignoreLF, term);
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
return implReadLine(ignoreLF, term);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String implReadLine(boolean ignoreLF, boolean[] term) throws IOException {
|
||||
StringBuilder s = null;
|
||||
int startChar;
|
||||
|
||||
synchronized (lock) {
|
||||
ensureOpen();
|
||||
boolean omitLF = ignoreLF || skipLF;
|
||||
if (term != null) term[0] = false;
|
||||
ensureOpen();
|
||||
boolean omitLF = ignoreLF || skipLF;
|
||||
if (term != null) term[0] = false;
|
||||
|
||||
bufferLoop:
|
||||
for (;;) {
|
||||
bufferLoop:
|
||||
for (;;) {
|
||||
|
||||
if (nextChar >= nChars)
|
||||
fill();
|
||||
if (nextChar >= nChars) { /* EOF */
|
||||
if (s != null && s.length() > 0)
|
||||
return s.toString();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
boolean eol = false;
|
||||
char c = 0;
|
||||
int i;
|
||||
|
||||
/* Skip a leftover '\n', if necessary */
|
||||
if (omitLF && (cb[nextChar] == '\n'))
|
||||
nextChar++;
|
||||
skipLF = false;
|
||||
omitLF = false;
|
||||
|
||||
charLoop:
|
||||
for (i = nextChar; i < nChars; i++) {
|
||||
c = cb[i];
|
||||
if ((c == '\n') || (c == '\r')) {
|
||||
if (term != null) term[0] = true;
|
||||
eol = true;
|
||||
break charLoop;
|
||||
}
|
||||
}
|
||||
|
||||
startChar = nextChar;
|
||||
nextChar = i;
|
||||
|
||||
if (eol) {
|
||||
String str;
|
||||
if (s == null) {
|
||||
str = new String(cb, startChar, i - startChar);
|
||||
} else {
|
||||
s.append(cb, startChar, i - startChar);
|
||||
str = s.toString();
|
||||
}
|
||||
nextChar++;
|
||||
if (c == '\r') {
|
||||
skipLF = true;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
if (s == null)
|
||||
s = new StringBuilder(defaultExpectedLineLength);
|
||||
s.append(cb, startChar, i - startChar);
|
||||
if (nextChar >= nChars)
|
||||
fill();
|
||||
if (nextChar >= nChars) { /* EOF */
|
||||
if (s != null && s.length() > 0)
|
||||
return s.toString();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
boolean eol = false;
|
||||
char c = 0;
|
||||
int i;
|
||||
|
||||
/* Skip a leftover '\n', if necessary */
|
||||
if (omitLF && (cb[nextChar] == '\n'))
|
||||
nextChar++;
|
||||
skipLF = false;
|
||||
omitLF = false;
|
||||
|
||||
charLoop:
|
||||
for (i = nextChar; i < nChars; i++) {
|
||||
c = cb[i];
|
||||
if ((c == '\n') || (c == '\r')) {
|
||||
if (term != null) term[0] = true;
|
||||
eol = true;
|
||||
break charLoop;
|
||||
}
|
||||
}
|
||||
|
||||
startChar = nextChar;
|
||||
nextChar = i;
|
||||
|
||||
if (eol) {
|
||||
String str;
|
||||
if (s == null) {
|
||||
str = new String(cb, startChar, i - startChar);
|
||||
} else {
|
||||
s.append(cb, startChar, i - startChar);
|
||||
str = s.toString();
|
||||
}
|
||||
nextChar++;
|
||||
if (c == '\r') {
|
||||
skipLF = true;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
if (s == null)
|
||||
s = new StringBuilder(defaultExpectedLineLength);
|
||||
s.append(cb, startChar, i - startChar);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -403,33 +444,47 @@ public class BufferedReader extends Reader {
|
|||
if (n < 0L) {
|
||||
throw new IllegalArgumentException("skip value is negative");
|
||||
}
|
||||
synchronized (lock) {
|
||||
ensureOpen();
|
||||
long r = n;
|
||||
while (r > 0) {
|
||||
if (nextChar >= nChars)
|
||||
fill();
|
||||
if (nextChar >= nChars) /* EOF */
|
||||
break;
|
||||
if (skipLF) {
|
||||
skipLF = false;
|
||||
if (cb[nextChar] == '\n') {
|
||||
nextChar++;
|
||||
}
|
||||
}
|
||||
long d = nChars - nextChar;
|
||||
if (r <= d) {
|
||||
nextChar += r;
|
||||
r = 0;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
r -= d;
|
||||
nextChar = nChars;
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
return implSkip(n);
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
return implSkip(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private long implSkip(long n) throws IOException {
|
||||
ensureOpen();
|
||||
long r = n;
|
||||
while (r > 0) {
|
||||
if (nextChar >= nChars)
|
||||
fill();
|
||||
if (nextChar >= nChars) /* EOF */
|
||||
break;
|
||||
if (skipLF) {
|
||||
skipLF = false;
|
||||
if (cb[nextChar] == '\n') {
|
||||
nextChar++;
|
||||
}
|
||||
}
|
||||
return n - r;
|
||||
long d = nChars - nextChar;
|
||||
if (r <= d) {
|
||||
nextChar += r;
|
||||
r = 0;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
r -= d;
|
||||
nextChar = nChars;
|
||||
}
|
||||
}
|
||||
return n - r;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -440,30 +495,44 @@ public class BufferedReader extends Reader {
|
|||
* @throws IOException If an I/O error occurs
|
||||
*/
|
||||
public boolean ready() throws IOException {
|
||||
synchronized (lock) {
|
||||
ensureOpen();
|
||||
|
||||
/*
|
||||
* If newline needs to be skipped and the next char to be read
|
||||
* is a newline character, then just skip it right away.
|
||||
*/
|
||||
if (skipLF) {
|
||||
/* Note that in.ready() will return true if and only if the next
|
||||
* read on the stream will not block.
|
||||
*/
|
||||
if (nextChar >= nChars && in.ready()) {
|
||||
fill();
|
||||
}
|
||||
if (nextChar < nChars) {
|
||||
if (cb[nextChar] == '\n')
|
||||
nextChar++;
|
||||
skipLF = false;
|
||||
}
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
return implReady();
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
return implReady();
|
||||
}
|
||||
return (nextChar < nChars) || in.ready();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean implReady() throws IOException {
|
||||
ensureOpen();
|
||||
|
||||
/*
|
||||
* If newline needs to be skipped and the next char to be read
|
||||
* is a newline character, then just skip it right away.
|
||||
*/
|
||||
if (skipLF) {
|
||||
/* Note that in.ready() will return true if and only if the next
|
||||
* read on the stream will not block.
|
||||
*/
|
||||
if (nextChar >= nChars && in.ready()) {
|
||||
fill();
|
||||
}
|
||||
if (nextChar < nChars) {
|
||||
if (cb[nextChar] == '\n')
|
||||
nextChar++;
|
||||
skipLF = false;
|
||||
}
|
||||
}
|
||||
return (nextChar < nChars) || in.ready();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether this stream supports the mark() operation, which it does.
|
||||
*/
|
||||
|
@ -491,14 +560,28 @@ public class BufferedReader extends Reader {
|
|||
if (readAheadLimit < 0) {
|
||||
throw new IllegalArgumentException("Read-ahead limit < 0");
|
||||
}
|
||||
synchronized (lock) {
|
||||
ensureOpen();
|
||||
this.readAheadLimit = readAheadLimit;
|
||||
markedChar = nextChar;
|
||||
markedSkipLF = skipLF;
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
implMark(readAheadLimit);
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
implMark(readAheadLimit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void implMark(int readAheadLimit) throws IOException {
|
||||
ensureOpen();
|
||||
this.readAheadLimit = readAheadLimit;
|
||||
markedChar = nextChar;
|
||||
markedSkipLF = skipLF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the stream to the most recent mark.
|
||||
*
|
||||
|
@ -506,27 +589,55 @@ public class BufferedReader extends Reader {
|
|||
* or if the mark has been invalidated
|
||||
*/
|
||||
public void reset() throws IOException {
|
||||
synchronized (lock) {
|
||||
ensureOpen();
|
||||
if (markedChar < 0)
|
||||
throw new IOException((markedChar == INVALIDATED)
|
||||
? "Mark invalid"
|
||||
: "Stream not marked");
|
||||
nextChar = markedChar;
|
||||
skipLF = markedSkipLF;
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
implReset();
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
implReset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void implReset() throws IOException {
|
||||
ensureOpen();
|
||||
if (markedChar < 0)
|
||||
throw new IOException((markedChar == INVALIDATED)
|
||||
? "Mark invalid"
|
||||
: "Stream not marked");
|
||||
nextChar = markedChar;
|
||||
skipLF = markedSkipLF;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
synchronized (lock) {
|
||||
if (in == null)
|
||||
return;
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
in.close();
|
||||
implClose();
|
||||
} finally {
|
||||
in = null;
|
||||
cb = null;
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
implClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void implClose() throws IOException {
|
||||
if (in == null)
|
||||
return;
|
||||
try {
|
||||
in.close();
|
||||
} finally {
|
||||
in = null;
|
||||
cb = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -25,6 +25,10 @@
|
|||
|
||||
package java.io;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import jdk.internal.misc.InternalLock;
|
||||
import jdk.internal.misc.VM;
|
||||
|
||||
/**
|
||||
* Writes text to a character-output stream, buffering characters so as to
|
||||
|
@ -64,13 +68,40 @@ package java.io;
|
|||
*/
|
||||
|
||||
public class BufferedWriter extends Writer {
|
||||
private static final int DEFAULT_INITIAL_BUFFER_SIZE = 512;
|
||||
private static final int DEFAULT_MAX_BUFFER_SIZE = 8192;
|
||||
|
||||
private Writer out;
|
||||
|
||||
private char cb[];
|
||||
private int nChars, nextChar;
|
||||
private final int maxChars; // maximum number of buffers chars
|
||||
|
||||
private static int defaultCharBufferSize = 8192;
|
||||
/**
|
||||
* Returns the buffer size to use when no output buffer size specified
|
||||
*/
|
||||
private static int initialBufferSize() {
|
||||
if (VM.isBooted() && Thread.currentThread().isVirtual()) {
|
||||
return DEFAULT_INITIAL_BUFFER_SIZE;
|
||||
} else {
|
||||
return DEFAULT_MAX_BUFFER_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a buffered character-output stream.
|
||||
*/
|
||||
private BufferedWriter(Writer out, int initialSize, int maxSize) {
|
||||
super(out);
|
||||
if (initialSize <= 0) {
|
||||
throw new IllegalArgumentException("Buffer size <= 0");
|
||||
}
|
||||
|
||||
this.out = out;
|
||||
this.cb = new char[initialSize];
|
||||
this.nChars = initialSize;
|
||||
this.maxChars = maxSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a buffered character-output stream that uses a default-sized
|
||||
|
@ -79,7 +110,7 @@ public class BufferedWriter extends Writer {
|
|||
* @param out A Writer
|
||||
*/
|
||||
public BufferedWriter(Writer out) {
|
||||
this(out, defaultCharBufferSize);
|
||||
this(out, initialBufferSize(), DEFAULT_MAX_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -92,13 +123,7 @@ public class BufferedWriter extends Writer {
|
|||
* @throws IllegalArgumentException If {@code sz <= 0}
|
||||
*/
|
||||
public BufferedWriter(Writer out, int sz) {
|
||||
super(out);
|
||||
if (sz <= 0)
|
||||
throw new IllegalArgumentException("Buffer size <= 0");
|
||||
this.out = out;
|
||||
cb = new char[sz];
|
||||
nChars = sz;
|
||||
nextChar = 0;
|
||||
this(out, sz, sz);
|
||||
}
|
||||
|
||||
/** Checks to make sure that the stream has not been closed */
|
||||
|
@ -107,35 +132,82 @@ public class BufferedWriter extends Writer {
|
|||
throw new IOException("Stream closed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Grow char array to fit an additional len characters if needed.
|
||||
* If possible, it grows by len+1 to avoid flushing when len chars
|
||||
* are added.
|
||||
*
|
||||
* This method should only be called while holding the lock.
|
||||
*/
|
||||
private void growIfNeeded(int len) {
|
||||
int neededSize = nextChar + len + 1;
|
||||
if (neededSize < 0)
|
||||
neededSize = Integer.MAX_VALUE;
|
||||
if (neededSize > nChars && nChars < maxChars) {
|
||||
int newSize = min(neededSize, maxChars);
|
||||
cb = Arrays.copyOf(cb, newSize);
|
||||
nChars = newSize;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the output buffer to the underlying character stream, without
|
||||
* flushing the stream itself. This method is non-private only so that it
|
||||
* may be invoked by PrintStream.
|
||||
*/
|
||||
void flushBuffer() throws IOException {
|
||||
synchronized (lock) {
|
||||
ensureOpen();
|
||||
if (nextChar == 0)
|
||||
return;
|
||||
out.write(cb, 0, nextChar);
|
||||
nextChar = 0;
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
implFlushBuffer();
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
implFlushBuffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void implFlushBuffer() throws IOException {
|
||||
ensureOpen();
|
||||
if (nextChar == 0)
|
||||
return;
|
||||
out.write(cb, 0, nextChar);
|
||||
nextChar = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a single character.
|
||||
*
|
||||
* @throws IOException If an I/O error occurs
|
||||
*/
|
||||
public void write(int c) throws IOException {
|
||||
synchronized (lock) {
|
||||
ensureOpen();
|
||||
if (nextChar >= nChars)
|
||||
flushBuffer();
|
||||
cb[nextChar++] = (char) c;
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
implWrite(c);
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
implWrite(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void implWrite(int c) throws IOException {
|
||||
ensureOpen();
|
||||
growIfNeeded(1);
|
||||
if (nextChar >= nChars)
|
||||
flushBuffer();
|
||||
cb[nextChar++] = (char) c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Our own little min method, to avoid loading java.lang.Math if we've run
|
||||
* out of file descriptors and we're trying to print a stack trace.
|
||||
|
@ -167,32 +239,46 @@ public class BufferedWriter extends Writer {
|
|||
* @throws IOException If an I/O error occurs
|
||||
*/
|
||||
public void write(char[] cbuf, int off, int len) throws IOException {
|
||||
synchronized (lock) {
|
||||
ensureOpen();
|
||||
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
|
||||
((off + len) > cbuf.length) || ((off + len) < 0)) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
} else if (len == 0) {
|
||||
return;
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
implWrite(cbuf, off, len);
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
implWrite(cbuf, off, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (len >= nChars) {
|
||||
/* If the request length exceeds the size of the output buffer,
|
||||
flush the buffer and then write the data directly. In this
|
||||
way buffered streams will cascade harmlessly. */
|
||||
private void implWrite(char[] cbuf, int off, int len) throws IOException {
|
||||
ensureOpen();
|
||||
Objects.checkFromIndexSize(off, len, cbuf.length);
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (len >= maxChars) {
|
||||
/* If the request length exceeds the max size of the output buffer,
|
||||
flush the buffer and then write the data directly. In this
|
||||
way buffered streams will cascade harmlessly. */
|
||||
flushBuffer();
|
||||
out.write(cbuf, off, len);
|
||||
return;
|
||||
}
|
||||
|
||||
growIfNeeded(len);
|
||||
int b = off, t = off + len;
|
||||
while (b < t) {
|
||||
int d = min(nChars - nextChar, t - b);
|
||||
System.arraycopy(cbuf, b, cb, nextChar, d);
|
||||
b += d;
|
||||
nextChar += d;
|
||||
if (nextChar >= nChars) {
|
||||
flushBuffer();
|
||||
out.write(cbuf, off, len);
|
||||
return;
|
||||
}
|
||||
|
||||
int b = off, t = off + len;
|
||||
while (b < t) {
|
||||
int d = min(nChars - nextChar, t - b);
|
||||
System.arraycopy(cbuf, b, cb, nextChar, d);
|
||||
b += d;
|
||||
nextChar += d;
|
||||
if (nextChar >= nChars)
|
||||
flushBuffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -220,18 +306,32 @@ public class BufferedWriter extends Writer {
|
|||
* @throws IOException If an I/O error occurs
|
||||
*/
|
||||
public void write(String s, int off, int len) throws IOException {
|
||||
synchronized (lock) {
|
||||
ensureOpen();
|
||||
|
||||
int b = off, t = off + len;
|
||||
while (b < t) {
|
||||
int d = min(nChars - nextChar, t - b);
|
||||
s.getChars(b, b + d, cb, nextChar);
|
||||
b += d;
|
||||
nextChar += d;
|
||||
if (nextChar >= nChars)
|
||||
flushBuffer();
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
implWrite(s, off, len);
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
implWrite(s, off, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void implWrite(String s, int off, int len) throws IOException {
|
||||
ensureOpen();
|
||||
growIfNeeded(len);
|
||||
int b = off, t = off + len;
|
||||
while (b < t) {
|
||||
int d = min(nChars - nextChar, t - b);
|
||||
s.getChars(b, b + d, cb, nextChar);
|
||||
b += d;
|
||||
nextChar += d;
|
||||
if (nextChar >= nChars)
|
||||
flushBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -252,24 +352,52 @@ public class BufferedWriter extends Writer {
|
|||
* @throws IOException If an I/O error occurs
|
||||
*/
|
||||
public void flush() throws IOException {
|
||||
synchronized (lock) {
|
||||
flushBuffer();
|
||||
out.flush();
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
implFlush();
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
implFlush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void implFlush() throws IOException {
|
||||
flushBuffer();
|
||||
out.flush();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
implClose();
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
implClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("try")
|
||||
public void close() throws IOException {
|
||||
synchronized (lock) {
|
||||
if (out == null) {
|
||||
return;
|
||||
}
|
||||
try (Writer w = out) {
|
||||
flushBuffer();
|
||||
} finally {
|
||||
out = null;
|
||||
cb = null;
|
||||
}
|
||||
private void implClose() throws IOException {
|
||||
if (out == null) {
|
||||
return;
|
||||
}
|
||||
try (Writer w = out) {
|
||||
flushBuffer();
|
||||
} finally {
|
||||
out = null;
|
||||
cb = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1994, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1994, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -27,6 +27,7 @@ package java.io;
|
|||
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.Arrays;
|
||||
import jdk.internal.misc.Blocker;
|
||||
import jdk.internal.util.ArraysSupport;
|
||||
import sun.nio.ch.FileChannelImpl;
|
||||
|
||||
|
@ -213,7 +214,12 @@ public class FileInputStream extends InputStream
|
|||
* @param name the name of the file
|
||||
*/
|
||||
private void open(String name) throws FileNotFoundException {
|
||||
open0(name);
|
||||
long comp = Blocker.begin();
|
||||
try {
|
||||
open0(name);
|
||||
} finally {
|
||||
Blocker.end(comp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -225,7 +231,12 @@ public class FileInputStream extends InputStream
|
|||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public int read() throws IOException {
|
||||
return read0();
|
||||
long comp = Blocker.begin();
|
||||
try {
|
||||
return read0();
|
||||
} finally {
|
||||
Blocker.end(comp);
|
||||
}
|
||||
}
|
||||
|
||||
private native int read0() throws IOException;
|
||||
|
@ -251,7 +262,12 @@ public class FileInputStream extends InputStream
|
|||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public int read(byte[] b) throws IOException {
|
||||
return readBytes(b, 0, b.length);
|
||||
long comp = Blocker.begin();
|
||||
try {
|
||||
return readBytes(b, 0, b.length);
|
||||
} finally {
|
||||
Blocker.end(comp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -273,7 +289,12 @@ public class FileInputStream extends InputStream
|
|||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
return readBytes(b, off, len);
|
||||
long comp = Blocker.begin();
|
||||
try {
|
||||
return readBytes(b, off, len);
|
||||
} finally {
|
||||
Blocker.end(comp);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] readAllBytes() throws IOException {
|
||||
|
@ -373,12 +394,22 @@ public class FileInputStream extends InputStream
|
|||
}
|
||||
|
||||
private long length() throws IOException {
|
||||
return length0();
|
||||
long comp = Blocker.begin();
|
||||
try {
|
||||
return length0();
|
||||
} finally {
|
||||
Blocker.end(comp);
|
||||
}
|
||||
}
|
||||
private native long length0() throws IOException;
|
||||
|
||||
private long position() throws IOException {
|
||||
return position0();
|
||||
long comp = Blocker.begin();
|
||||
try {
|
||||
return position0();
|
||||
} finally {
|
||||
Blocker.end(comp);
|
||||
}
|
||||
}
|
||||
private native long position0() throws IOException;
|
||||
|
||||
|
@ -407,7 +438,12 @@ public class FileInputStream extends InputStream
|
|||
* support seek, or if an I/O error occurs.
|
||||
*/
|
||||
public long skip(long n) throws IOException {
|
||||
return skip0(n);
|
||||
long comp = Blocker.begin();
|
||||
try {
|
||||
return skip0(n);
|
||||
} finally {
|
||||
Blocker.end(comp);
|
||||
}
|
||||
}
|
||||
|
||||
private native long skip0(long n) throws IOException;
|
||||
|
@ -430,7 +466,12 @@ public class FileInputStream extends InputStream
|
|||
* {@code close} or an I/O error occurs.
|
||||
*/
|
||||
public int available() throws IOException {
|
||||
return available0();
|
||||
long comp = Blocker.begin();
|
||||
try {
|
||||
return available0();
|
||||
} finally {
|
||||
Blocker.end(comp);
|
||||
}
|
||||
}
|
||||
|
||||
private native int available0() throws IOException;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1994, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1994, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -28,6 +28,7 @@ package java.io;
|
|||
import java.nio.channels.FileChannel;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.access.JavaIOFileDescriptorAccess;
|
||||
import jdk.internal.misc.Blocker;
|
||||
import sun.nio.ch.FileChannelImpl;
|
||||
|
||||
|
||||
|
@ -288,9 +289,13 @@ public class FileOutputStream extends OutputStream
|
|||
* @param name name of file to be opened
|
||||
* @param append whether the file is to be opened in append mode
|
||||
*/
|
||||
private void open(String name, boolean append)
|
||||
throws FileNotFoundException {
|
||||
open0(name, append);
|
||||
private void open(String name, boolean append) throws FileNotFoundException {
|
||||
long comp = Blocker.begin();
|
||||
try {
|
||||
open0(name, append);
|
||||
} finally {
|
||||
Blocker.end(comp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -310,7 +315,13 @@ public class FileOutputStream extends OutputStream
|
|||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public void write(int b) throws IOException {
|
||||
write(b, fdAccess.getAppend(fd));
|
||||
boolean append = fdAccess.getAppend(fd);
|
||||
long comp = Blocker.begin();
|
||||
try {
|
||||
write(b, append);
|
||||
} finally {
|
||||
Blocker.end(comp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -333,7 +344,13 @@ public class FileOutputStream extends OutputStream
|
|||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public void write(byte[] b) throws IOException {
|
||||
writeBytes(b, 0, b.length, fdAccess.getAppend(fd));
|
||||
boolean append = fdAccess.getAppend(fd);
|
||||
long comp = Blocker.begin();
|
||||
try {
|
||||
writeBytes(b, 0, b.length, append);
|
||||
} finally {
|
||||
Blocker.end(comp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -346,7 +363,13 @@ public class FileOutputStream extends OutputStream
|
|||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
writeBytes(b, off, len, fdAccess.getAppend(fd));
|
||||
boolean append = fdAccess.getAppend(fd);
|
||||
long comp = Blocker.begin();
|
||||
try {
|
||||
writeBytes(b, off, len, append);
|
||||
} finally {
|
||||
Blocker.end(comp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -28,9 +28,9 @@ package java.io;
|
|||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import jdk.internal.misc.InternalLock;
|
||||
import sun.nio.cs.StreamDecoder;
|
||||
|
||||
|
||||
/**
|
||||
* An InputStreamReader is a bridge from byte streams to character streams: It
|
||||
* reads bytes and decodes them into characters using a specified {@link
|
||||
|
@ -61,9 +61,22 @@ import sun.nio.cs.StreamDecoder;
|
|||
*/
|
||||
|
||||
public class InputStreamReader extends Reader {
|
||||
|
||||
private final StreamDecoder sd;
|
||||
|
||||
/**
|
||||
* Return the lock object for the given reader's stream decoder.
|
||||
* If the reader type is trusted then an internal lock can be used. If the
|
||||
* reader type is not trusted then the reader object is the lock.
|
||||
*/
|
||||
private static Object lockFor(InputStreamReader reader) {
|
||||
Class<?> clazz = reader.getClass();
|
||||
if (clazz == InputStreamReader.class || clazz == FileReader.class) {
|
||||
return InternalLock.newLockOr(reader);
|
||||
} else {
|
||||
return reader;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an InputStreamReader that uses the
|
||||
* {@link Charset#defaultCharset() default charset}.
|
||||
|
@ -74,8 +87,8 @@ public class InputStreamReader extends Reader {
|
|||
*/
|
||||
public InputStreamReader(InputStream in) {
|
||||
super(in);
|
||||
sd = StreamDecoder.forInputStreamReader(in, this,
|
||||
Charset.defaultCharset()); // ## check lock object
|
||||
Charset cs = Charset.defaultCharset();
|
||||
sd = StreamDecoder.forInputStreamReader(in, lockFor(this), cs);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -96,7 +109,7 @@ public class InputStreamReader extends Reader {
|
|||
super(in);
|
||||
if (charsetName == null)
|
||||
throw new NullPointerException("charsetName");
|
||||
sd = StreamDecoder.forInputStreamReader(in, this, charsetName);
|
||||
sd = StreamDecoder.forInputStreamReader(in, lockFor(this), charsetName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -111,7 +124,7 @@ public class InputStreamReader extends Reader {
|
|||
super(in);
|
||||
if (cs == null)
|
||||
throw new NullPointerException("charset");
|
||||
sd = StreamDecoder.forInputStreamReader(in, this, cs);
|
||||
sd = StreamDecoder.forInputStreamReader(in, lockFor(this), cs);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -126,7 +139,7 @@ public class InputStreamReader extends Reader {
|
|||
super(in);
|
||||
if (dec == null)
|
||||
throw new NullPointerException("charset decoder");
|
||||
sd = StreamDecoder.forInputStreamReader(in, this, dec);
|
||||
sd = StreamDecoder.forInputStreamReader(in, lockFor(this), dec);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -28,9 +28,9 @@ package java.io;
|
|||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.CharsetEncoder;
|
||||
import jdk.internal.misc.InternalLock;
|
||||
import sun.nio.cs.StreamEncoder;
|
||||
|
||||
|
||||
/**
|
||||
* An OutputStreamWriter is a bridge from character streams to byte streams:
|
||||
* Characters written to it are encoded into bytes using a specified {@link
|
||||
|
@ -74,9 +74,22 @@ import sun.nio.cs.StreamEncoder;
|
|||
*/
|
||||
|
||||
public class OutputStreamWriter extends Writer {
|
||||
|
||||
private final StreamEncoder se;
|
||||
|
||||
/**
|
||||
* Return the lock object for the given writer's stream encoder.
|
||||
* If the writer type is trusted then an internal lock can be used. If the
|
||||
* writer type is not trusted then the writer object is the lock.
|
||||
*/
|
||||
private static Object lockFor(OutputStreamWriter writer) {
|
||||
Class<?> clazz = writer.getClass();
|
||||
if (clazz == OutputStreamWriter.class || clazz == FileWriter.class) {
|
||||
return InternalLock.newLockOr(writer);
|
||||
} else {
|
||||
return writer;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an OutputStreamWriter that uses the named charset.
|
||||
*
|
||||
|
@ -95,7 +108,7 @@ public class OutputStreamWriter extends Writer {
|
|||
super(out);
|
||||
if (charsetName == null)
|
||||
throw new NullPointerException("charsetName");
|
||||
se = StreamEncoder.forOutputStreamWriter(out, this, charsetName);
|
||||
se = StreamEncoder.forOutputStreamWriter(out, lockFor(this), charsetName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -108,7 +121,7 @@ public class OutputStreamWriter extends Writer {
|
|||
*/
|
||||
public OutputStreamWriter(OutputStream out) {
|
||||
super(out);
|
||||
se = StreamEncoder.forOutputStreamWriter(out, this,
|
||||
se = StreamEncoder.forOutputStreamWriter(out, lockFor(this),
|
||||
out instanceof PrintStream ps ? ps.charset() : Charset.defaultCharset());
|
||||
}
|
||||
|
||||
|
@ -127,7 +140,7 @@ public class OutputStreamWriter extends Writer {
|
|||
super(out);
|
||||
if (cs == null)
|
||||
throw new NullPointerException("charset");
|
||||
se = StreamEncoder.forOutputStreamWriter(out, this, cs);
|
||||
se = StreamEncoder.forOutputStreamWriter(out, lockFor(this), cs);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -145,7 +158,7 @@ public class OutputStreamWriter extends Writer {
|
|||
super(out);
|
||||
if (enc == null)
|
||||
throw new NullPointerException("charset encoder");
|
||||
se = StreamEncoder.forOutputStreamWriter(out, this, enc);
|
||||
se = StreamEncoder.forOutputStreamWriter(out, lockFor(this), enc);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -30,6 +30,9 @@ import java.util.Locale;
|
|||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.IllegalCharsetNameException;
|
||||
import java.nio.charset.UnsupportedCharsetException;
|
||||
import jdk.internal.access.JavaIOPrintStreamAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.InternalLock;
|
||||
|
||||
/**
|
||||
* A {@code PrintStream} adds functionality to another output stream,
|
||||
|
@ -64,6 +67,8 @@ import java.nio.charset.UnsupportedCharsetException;
|
|||
public class PrintStream extends FilterOutputStream
|
||||
implements Appendable, Closeable
|
||||
{
|
||||
// initialized to null when PrintStream is sub-classed
|
||||
private final InternalLock lock;
|
||||
|
||||
private final boolean autoFlush;
|
||||
private boolean trouble = false;
|
||||
|
@ -112,6 +117,13 @@ public class PrintStream extends FilterOutputStream
|
|||
this.charset = out instanceof PrintStream ps ? ps.charset() : Charset.defaultCharset();
|
||||
this.charOut = new OutputStreamWriter(this, charset);
|
||||
this.textOut = new BufferedWriter(charOut);
|
||||
|
||||
// use monitors when PrintStream is sub-classed
|
||||
if (getClass() == PrintStream.class) {
|
||||
lock = InternalLock.newLockOrNull();
|
||||
} else {
|
||||
lock = null;
|
||||
}
|
||||
}
|
||||
|
||||
/* Variant of the private constructor so that the given charset name
|
||||
|
@ -206,6 +218,13 @@ public class PrintStream extends FilterOutputStream
|
|||
this.charOut = new OutputStreamWriter(this, charset);
|
||||
this.textOut = new BufferedWriter(charOut);
|
||||
this.charset = charset;
|
||||
|
||||
// use monitors when PrintStream is sub-classed
|
||||
if (getClass() == PrintStream.class) {
|
||||
lock = InternalLock.newLockOrNull();
|
||||
} else {
|
||||
lock = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -425,17 +444,30 @@ public class PrintStream extends FilterOutputStream
|
|||
*/
|
||||
@Override
|
||||
public void flush() {
|
||||
synchronized (this) {
|
||||
if (lock != null) {
|
||||
lock.lock();
|
||||
try {
|
||||
ensureOpen();
|
||||
out.flush();
|
||||
implFlush();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
catch (IOException x) {
|
||||
trouble = true;
|
||||
} else {
|
||||
synchronized (this) {
|
||||
implFlush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void implFlush() {
|
||||
try {
|
||||
ensureOpen();
|
||||
out.flush();
|
||||
}
|
||||
catch (IOException x) {
|
||||
trouble = true;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean closing = false; /* To avoid recursive closing */
|
||||
|
||||
/**
|
||||
|
@ -446,20 +478,33 @@ public class PrintStream extends FilterOutputStream
|
|||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
synchronized (this) {
|
||||
if (! closing) {
|
||||
closing = true;
|
||||
try {
|
||||
textOut.close();
|
||||
out.close();
|
||||
}
|
||||
catch (IOException x) {
|
||||
trouble = true;
|
||||
}
|
||||
textOut = null;
|
||||
charOut = null;
|
||||
out = null;
|
||||
if (lock != null) {
|
||||
lock.lock();
|
||||
try {
|
||||
implClose();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (this) {
|
||||
implClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void implClose() {
|
||||
if (!closing) {
|
||||
closing = true;
|
||||
try {
|
||||
textOut.close();
|
||||
out.close();
|
||||
}
|
||||
catch (IOException x) {
|
||||
trouble = true;
|
||||
}
|
||||
textOut = null;
|
||||
charOut = null;
|
||||
out = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -526,11 +571,17 @@ public class PrintStream extends FilterOutputStream
|
|||
@Override
|
||||
public void write(int b) {
|
||||
try {
|
||||
synchronized (this) {
|
||||
ensureOpen();
|
||||
out.write(b);
|
||||
if ((b == '\n') && autoFlush)
|
||||
out.flush();
|
||||
if (lock != null) {
|
||||
lock.lock();
|
||||
try {
|
||||
implWrite(b);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (this) {
|
||||
implWrite(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InterruptedIOException x) {
|
||||
|
@ -541,6 +592,13 @@ public class PrintStream extends FilterOutputStream
|
|||
}
|
||||
}
|
||||
|
||||
private void implWrite(int b) throws IOException {
|
||||
ensureOpen();
|
||||
out.write(b);
|
||||
if ((b == '\n') && autoFlush)
|
||||
out.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes {@code len} bytes from the specified byte array starting at
|
||||
* offset {@code off} to this stream. If automatic flushing is
|
||||
|
@ -558,11 +616,17 @@ public class PrintStream extends FilterOutputStream
|
|||
@Override
|
||||
public void write(byte[] buf, int off, int len) {
|
||||
try {
|
||||
synchronized (this) {
|
||||
ensureOpen();
|
||||
out.write(buf, off, len);
|
||||
if (autoFlush)
|
||||
out.flush();
|
||||
if (lock != null) {
|
||||
lock.lock();
|
||||
try {
|
||||
implWrite(buf, off, len);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (this) {
|
||||
implWrite(buf, off, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InterruptedIOException x) {
|
||||
|
@ -573,6 +637,14 @@ public class PrintStream extends FilterOutputStream
|
|||
}
|
||||
}
|
||||
|
||||
private void implWrite(byte[] buf, int off, int len) throws IOException {
|
||||
ensureOpen();
|
||||
out.write(buf, off, len);
|
||||
if (autoFlush)
|
||||
out.flush();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes all bytes from the specified byte array to this stream. If
|
||||
* automatic flushing is enabled then the {@code flush} method will be
|
||||
|
@ -639,17 +711,16 @@ public class PrintStream extends FilterOutputStream
|
|||
|
||||
private void write(char[] buf) {
|
||||
try {
|
||||
synchronized (this) {
|
||||
ensureOpen();
|
||||
textOut.write(buf);
|
||||
textOut.flushBuffer();
|
||||
charOut.flushBuffer();
|
||||
if (autoFlush) {
|
||||
for (int i = 0; i < buf.length; i++)
|
||||
if (buf[i] == '\n') {
|
||||
out.flush();
|
||||
break;
|
||||
}
|
||||
if (lock != null) {
|
||||
lock.lock();
|
||||
try {
|
||||
implWrite(buf);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (this) {
|
||||
implWrite(buf);
|
||||
}
|
||||
}
|
||||
} catch (InterruptedIOException x) {
|
||||
|
@ -659,20 +730,37 @@ public class PrintStream extends FilterOutputStream
|
|||
}
|
||||
}
|
||||
|
||||
private void implWrite(char[] buf) throws IOException {
|
||||
ensureOpen();
|
||||
textOut.write(buf);
|
||||
textOut.flushBuffer();
|
||||
charOut.flushBuffer();
|
||||
if (autoFlush) {
|
||||
for (int i = 0; i < buf.length; i++)
|
||||
if (buf[i] == '\n') {
|
||||
out.flush();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Used to optimize away back-to-back flushing and synchronization when
|
||||
// using println, but since subclasses could exist which depend on
|
||||
// observing a call to print followed by newLine() we only use this if
|
||||
// getClass() == PrintStream.class to avoid compatibility issues.
|
||||
private void writeln(char[] buf) {
|
||||
try {
|
||||
synchronized (this) {
|
||||
ensureOpen();
|
||||
textOut.write(buf);
|
||||
textOut.newLine();
|
||||
textOut.flushBuffer();
|
||||
charOut.flushBuffer();
|
||||
if (autoFlush)
|
||||
out.flush();
|
||||
if (lock != null) {
|
||||
lock.lock();
|
||||
try {
|
||||
implWriteln(buf);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (this) {
|
||||
implWriteln(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InterruptedIOException x) {
|
||||
|
@ -683,15 +771,29 @@ public class PrintStream extends FilterOutputStream
|
|||
}
|
||||
}
|
||||
|
||||
private void implWriteln(char[] buf) throws IOException {
|
||||
ensureOpen();
|
||||
textOut.write(buf);
|
||||
textOut.newLine();
|
||||
textOut.flushBuffer();
|
||||
charOut.flushBuffer();
|
||||
if (autoFlush)
|
||||
out.flush();
|
||||
}
|
||||
|
||||
private void write(String s) {
|
||||
try {
|
||||
synchronized (this) {
|
||||
ensureOpen();
|
||||
textOut.write(s);
|
||||
textOut.flushBuffer();
|
||||
charOut.flushBuffer();
|
||||
if (autoFlush && (s.indexOf('\n') >= 0))
|
||||
out.flush();
|
||||
if (lock != null) {
|
||||
lock.lock();
|
||||
try {
|
||||
implWrite(s);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (this) {
|
||||
implWrite(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InterruptedIOException x) {
|
||||
|
@ -702,20 +804,32 @@ public class PrintStream extends FilterOutputStream
|
|||
}
|
||||
}
|
||||
|
||||
private void implWrite(String s) throws IOException {
|
||||
ensureOpen();
|
||||
textOut.write(s);
|
||||
textOut.flushBuffer();
|
||||
charOut.flushBuffer();
|
||||
if (autoFlush && (s.indexOf('\n') >= 0))
|
||||
out.flush();
|
||||
}
|
||||
|
||||
// Used to optimize away back-to-back flushing and synchronization when
|
||||
// using println, but since subclasses could exist which depend on
|
||||
// observing a call to print followed by newLine we only use this if
|
||||
// getClass() == PrintStream.class to avoid compatibility issues.
|
||||
private void writeln(String s) {
|
||||
try {
|
||||
synchronized (this) {
|
||||
ensureOpen();
|
||||
textOut.write(s);
|
||||
textOut.newLine();
|
||||
textOut.flushBuffer();
|
||||
charOut.flushBuffer();
|
||||
if (autoFlush)
|
||||
out.flush();
|
||||
if (lock != null) {
|
||||
lock.lock();
|
||||
try {
|
||||
implWriteln(s);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (this) {
|
||||
implWriteln(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InterruptedIOException x) {
|
||||
|
@ -726,15 +840,29 @@ public class PrintStream extends FilterOutputStream
|
|||
}
|
||||
}
|
||||
|
||||
private void implWriteln(String s) throws IOException {
|
||||
ensureOpen();
|
||||
textOut.write(s);
|
||||
textOut.newLine();
|
||||
textOut.flushBuffer();
|
||||
charOut.flushBuffer();
|
||||
if (autoFlush)
|
||||
out.flush();
|
||||
}
|
||||
|
||||
private void newLine() {
|
||||
try {
|
||||
synchronized (this) {
|
||||
ensureOpen();
|
||||
textOut.newLine();
|
||||
textOut.flushBuffer();
|
||||
charOut.flushBuffer();
|
||||
if (autoFlush)
|
||||
out.flush();
|
||||
if (lock != null) {
|
||||
lock.lock();
|
||||
try {
|
||||
implNewLine();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (this) {
|
||||
implNewLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InterruptedIOException x) {
|
||||
|
@ -745,6 +873,15 @@ public class PrintStream extends FilterOutputStream
|
|||
}
|
||||
}
|
||||
|
||||
private void implNewLine() throws IOException {
|
||||
ensureOpen();
|
||||
textOut.newLine();
|
||||
textOut.flushBuffer();
|
||||
charOut.flushBuffer();
|
||||
if (autoFlush)
|
||||
out.flush();
|
||||
}
|
||||
|
||||
/* Methods that do not terminate lines */
|
||||
|
||||
/**
|
||||
|
@ -1202,14 +1339,17 @@ public class PrintStream extends FilterOutputStream
|
|||
*/
|
||||
public PrintStream format(String format, Object ... args) {
|
||||
try {
|
||||
synchronized (this) {
|
||||
ensureOpen();
|
||||
if ((formatter == null)
|
||||
|| (formatter.locale() !=
|
||||
Locale.getDefault(Locale.Category.FORMAT)))
|
||||
formatter = new Formatter((Appendable) this);
|
||||
formatter.format(Locale.getDefault(Locale.Category.FORMAT),
|
||||
format, args);
|
||||
if (lock != null) {
|
||||
lock.lock();
|
||||
try {
|
||||
implFormat(format, args);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (this) {
|
||||
implFormat(format, args);
|
||||
}
|
||||
}
|
||||
} catch (InterruptedIOException x) {
|
||||
Thread.currentThread().interrupt();
|
||||
|
@ -1219,6 +1359,13 @@ public class PrintStream extends FilterOutputStream
|
|||
return this;
|
||||
}
|
||||
|
||||
private void implFormat(String format, Object ... args) throws IOException {
|
||||
ensureOpen();
|
||||
if ((formatter == null) || (formatter.locale() != Locale.getDefault(Locale.Category.FORMAT)))
|
||||
formatter = new Formatter((Appendable) this);
|
||||
formatter.format(Locale.getDefault(Locale.Category.FORMAT), format, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a formatted string to this output stream using the specified
|
||||
* format string and arguments.
|
||||
|
@ -1261,12 +1408,17 @@ public class PrintStream extends FilterOutputStream
|
|||
*/
|
||||
public PrintStream format(Locale l, String format, Object ... args) {
|
||||
try {
|
||||
synchronized (this) {
|
||||
ensureOpen();
|
||||
if ((formatter == null)
|
||||
|| (formatter.locale() != l))
|
||||
formatter = new Formatter(this, l);
|
||||
formatter.format(l, format, args);
|
||||
if (lock != null) {
|
||||
lock.lock();
|
||||
try {
|
||||
implFormat(l, format, args);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (this) {
|
||||
implFormat(l, format, args);
|
||||
}
|
||||
}
|
||||
} catch (InterruptedIOException x) {
|
||||
Thread.currentThread().interrupt();
|
||||
|
@ -1276,6 +1428,13 @@ public class PrintStream extends FilterOutputStream
|
|||
return this;
|
||||
}
|
||||
|
||||
private void implFormat(Locale l, String format, Object ... args) throws IOException {
|
||||
ensureOpen();
|
||||
if ((formatter == null) || (formatter.locale() != l))
|
||||
formatter = new Formatter(this, l);
|
||||
formatter.format(l, format, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the specified character sequence to this output stream.
|
||||
*
|
||||
|
@ -1376,4 +1535,13 @@ public class PrintStream extends FilterOutputStream
|
|||
public Charset charset() {
|
||||
return charset;
|
||||
}
|
||||
|
||||
static {
|
||||
SharedSecrets.setJavaIOCPrintStreamAccess(new JavaIOPrintStreamAccess() {
|
||||
public Object lock(PrintStream ps) {
|
||||
Object lock = ps.lock;
|
||||
return (lock != null) ? lock : ps;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,9 @@ import java.util.Locale;
|
|||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.IllegalCharsetNameException;
|
||||
import java.nio.charset.UnsupportedCharsetException;
|
||||
import jdk.internal.access.JavaIOPrintWriterAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.InternalLock;
|
||||
|
||||
/**
|
||||
* Prints formatted representations of objects to a text-output stream. This
|
||||
|
@ -95,7 +98,7 @@ public class PrintWriter extends Writer {
|
|||
*
|
||||
* @param out A character-output stream
|
||||
*/
|
||||
public PrintWriter (Writer out) {
|
||||
public PrintWriter(Writer out) {
|
||||
this(out, false);
|
||||
}
|
||||
|
||||
|
@ -107,8 +110,7 @@ public class PrintWriter extends Writer {
|
|||
* {@code printf}, or {@code format} methods will
|
||||
* flush the output buffer
|
||||
*/
|
||||
public PrintWriter(Writer out,
|
||||
boolean autoFlush) {
|
||||
public PrintWriter(Writer out, boolean autoFlush) {
|
||||
super(out);
|
||||
this.out = out;
|
||||
this.autoFlush = autoFlush;
|
||||
|
@ -394,13 +396,26 @@ public class PrintWriter extends Writer {
|
|||
* @see #checkError()
|
||||
*/
|
||||
public void flush() {
|
||||
try {
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
implFlush();
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
ensureOpen();
|
||||
out.flush();
|
||||
implFlush();
|
||||
}
|
||||
}
|
||||
catch (IOException x) {
|
||||
}
|
||||
|
||||
private void implFlush() {
|
||||
try {
|
||||
ensureOpen();
|
||||
out.flush();
|
||||
} catch (IOException x) {
|
||||
trouble = true;
|
||||
}
|
||||
}
|
||||
|
@ -412,15 +427,28 @@ public class PrintWriter extends Writer {
|
|||
* @see #checkError()
|
||||
*/
|
||||
public void close() {
|
||||
try {
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
implClose();
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
if (out == null)
|
||||
return;
|
||||
implClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void implClose() {
|
||||
try {
|
||||
if (out != null) {
|
||||
out.close();
|
||||
out = null;
|
||||
}
|
||||
}
|
||||
catch (IOException x) {
|
||||
} catch (IOException x) {
|
||||
trouble = true;
|
||||
}
|
||||
}
|
||||
|
@ -478,16 +506,28 @@ public class PrintWriter extends Writer {
|
|||
* @param c int specifying a character to be written.
|
||||
*/
|
||||
public void write(int c) {
|
||||
try {
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
implWrite(c);
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
ensureOpen();
|
||||
out.write(c);
|
||||
implWrite(c);
|
||||
}
|
||||
}
|
||||
catch (InterruptedIOException x) {
|
||||
}
|
||||
|
||||
private void implWrite(int c) {
|
||||
try {
|
||||
ensureOpen();
|
||||
out.write(c);
|
||||
} catch (InterruptedIOException x) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
catch (IOException x) {
|
||||
} catch (IOException x) {
|
||||
trouble = true;
|
||||
}
|
||||
}
|
||||
|
@ -504,16 +544,28 @@ public class PrintWriter extends Writer {
|
|||
* to throw an {@code IndexOutOfBoundsException}
|
||||
*/
|
||||
public void write(char[] buf, int off, int len) {
|
||||
try {
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
implWrite(buf, off, len);
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
ensureOpen();
|
||||
out.write(buf, off, len);
|
||||
implWrite(buf, off, len);
|
||||
}
|
||||
}
|
||||
catch (InterruptedIOException x) {
|
||||
}
|
||||
|
||||
private void implWrite(char[] buf, int off, int len) {
|
||||
try {
|
||||
ensureOpen();
|
||||
out.write(buf, off, len);
|
||||
} catch (InterruptedIOException x) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
catch (IOException x) {
|
||||
} catch (IOException x) {
|
||||
trouble = true;
|
||||
}
|
||||
}
|
||||
|
@ -539,16 +591,28 @@ public class PrintWriter extends Writer {
|
|||
* to throw an {@code IndexOutOfBoundsException}
|
||||
*/
|
||||
public void write(String s, int off, int len) {
|
||||
try {
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
implWrite(s, off, len);
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
ensureOpen();
|
||||
out.write(s, off, len);
|
||||
implWrite(s, off, len);
|
||||
}
|
||||
}
|
||||
catch (InterruptedIOException x) {
|
||||
}
|
||||
|
||||
private void implWrite(String s, int off, int len) {
|
||||
try {
|
||||
ensureOpen();
|
||||
out.write(s, off, len);
|
||||
} catch (InterruptedIOException x) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
catch (IOException x) {
|
||||
} catch (IOException x) {
|
||||
trouble = true;
|
||||
}
|
||||
}
|
||||
|
@ -563,18 +627,30 @@ public class PrintWriter extends Writer {
|
|||
}
|
||||
|
||||
private void newLine() {
|
||||
try {
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
implNewLine();
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
ensureOpen();
|
||||
out.write(System.lineSeparator());
|
||||
if (autoFlush)
|
||||
out.flush();
|
||||
implNewLine();
|
||||
}
|
||||
}
|
||||
catch (InterruptedIOException x) {
|
||||
}
|
||||
|
||||
private void implNewLine() {
|
||||
try {
|
||||
ensureOpen();
|
||||
out.write(System.lineSeparator());
|
||||
if (autoFlush)
|
||||
out.flush();
|
||||
} catch (InterruptedIOException x) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
catch (IOException x) {
|
||||
} catch (IOException x) {
|
||||
trouble = true;
|
||||
}
|
||||
}
|
||||
|
@ -731,9 +807,20 @@ public class PrintWriter extends Writer {
|
|||
* @param x the {@code boolean} value to be printed
|
||||
*/
|
||||
public void println(boolean x) {
|
||||
synchronized (lock) {
|
||||
print(x);
|
||||
println();
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
print(x);
|
||||
println();
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
print(x);
|
||||
println();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -745,9 +832,20 @@ public class PrintWriter extends Writer {
|
|||
* @param x the {@code char} value to be printed
|
||||
*/
|
||||
public void println(char x) {
|
||||
synchronized (lock) {
|
||||
print(x);
|
||||
println();
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
print(x);
|
||||
println();
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
print(x);
|
||||
println();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -759,9 +857,20 @@ public class PrintWriter extends Writer {
|
|||
* @param x the {@code int} value to be printed
|
||||
*/
|
||||
public void println(int x) {
|
||||
synchronized (lock) {
|
||||
print(x);
|
||||
println();
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
print(x);
|
||||
println();
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
print(x);
|
||||
println();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -773,9 +882,20 @@ public class PrintWriter extends Writer {
|
|||
* @param x the {@code long} value to be printed
|
||||
*/
|
||||
public void println(long x) {
|
||||
synchronized (lock) {
|
||||
print(x);
|
||||
println();
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
print(x);
|
||||
println();
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
print(x);
|
||||
println();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -787,9 +907,20 @@ public class PrintWriter extends Writer {
|
|||
* @param x the {@code float} value to be printed
|
||||
*/
|
||||
public void println(float x) {
|
||||
synchronized (lock) {
|
||||
print(x);
|
||||
println();
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
print(x);
|
||||
println();
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
print(x);
|
||||
println();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -801,9 +932,20 @@ public class PrintWriter extends Writer {
|
|||
* @param x the {@code double} value to be printed
|
||||
*/
|
||||
public void println(double x) {
|
||||
synchronized (lock) {
|
||||
print(x);
|
||||
println();
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
print(x);
|
||||
println();
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
print(x);
|
||||
println();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -815,9 +957,20 @@ public class PrintWriter extends Writer {
|
|||
* @param x the array of {@code char} values to be printed
|
||||
*/
|
||||
public void println(char[] x) {
|
||||
synchronized (lock) {
|
||||
print(x);
|
||||
println();
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
print(x);
|
||||
println();
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
print(x);
|
||||
println();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -829,9 +982,20 @@ public class PrintWriter extends Writer {
|
|||
* @param x the {@code String} value to be printed
|
||||
*/
|
||||
public void println(String x) {
|
||||
synchronized (lock) {
|
||||
print(x);
|
||||
println();
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
print(x);
|
||||
println();
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
print(x);
|
||||
println();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -846,9 +1010,20 @@ public class PrintWriter extends Writer {
|
|||
*/
|
||||
public void println(Object x) {
|
||||
String s = String.valueOf(x);
|
||||
synchronized (lock) {
|
||||
print(s);
|
||||
println();
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
print(s);
|
||||
println();
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
print(s);
|
||||
println();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -994,22 +1169,36 @@ public class PrintWriter extends Writer {
|
|||
* @since 1.5
|
||||
*/
|
||||
public PrintWriter format(String format, Object ... args) {
|
||||
try {
|
||||
synchronized (lock) {
|
||||
ensureOpen();
|
||||
if ((formatter == null)
|
||||
|| (formatter.locale() != Locale.getDefault()))
|
||||
formatter = new Formatter(this);
|
||||
formatter.format(Locale.getDefault(), format, args);
|
||||
if (autoFlush)
|
||||
out.flush();
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
implFormat(format, args);
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
implFormat(format, args);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private void implFormat(String format, Object ... args) {
|
||||
try {
|
||||
ensureOpen();
|
||||
if ((formatter == null)
|
||||
|| (formatter.locale() != Locale.getDefault()))
|
||||
formatter = new Formatter(this);
|
||||
formatter.format(Locale.getDefault(), format, args);
|
||||
if (autoFlush)
|
||||
out.flush();
|
||||
} catch (InterruptedIOException x) {
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (IOException x) {
|
||||
trouble = true;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1054,21 +1243,35 @@ public class PrintWriter extends Writer {
|
|||
* @since 1.5
|
||||
*/
|
||||
public PrintWriter format(Locale l, String format, Object ... args) {
|
||||
try {
|
||||
synchronized (lock) {
|
||||
ensureOpen();
|
||||
if ((formatter == null) || (formatter.locale() != l))
|
||||
formatter = new Formatter(this, l);
|
||||
formatter.format(l, format, args);
|
||||
if (autoFlush)
|
||||
out.flush();
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
implFormat(l, format, args);
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
implFormat(l, format, args);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private void implFormat(Locale l, String format, Object ... args) {
|
||||
try {
|
||||
ensureOpen();
|
||||
if ((formatter == null) || (formatter.locale() != l))
|
||||
formatter = new Formatter(this, l);
|
||||
formatter.format(l, format, args);
|
||||
if (autoFlush)
|
||||
out.flush();
|
||||
} catch (InterruptedIOException x) {
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (IOException x) {
|
||||
trouble = true;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1161,4 +1364,12 @@ public class PrintWriter extends Writer {
|
|||
write(c);
|
||||
return this;
|
||||
}
|
||||
|
||||
static {
|
||||
SharedSecrets.setJavaIOCPrintWriterAccess(new JavaIOPrintWriterAccess() {
|
||||
public Object lock(PrintWriter pw) {
|
||||
return pw.lock;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
package java.io;
|
||||
|
||||
import java.util.Objects;
|
||||
import jdk.internal.misc.InternalLock;
|
||||
|
||||
/**
|
||||
* A {@code PushbackInputStream} adds
|
||||
|
@ -52,6 +53,8 @@ import java.util.Objects;
|
|||
* @since 1.0
|
||||
*/
|
||||
public class PushbackInputStream extends FilterInputStream {
|
||||
private final InternalLock closeLock;
|
||||
|
||||
/**
|
||||
* The pushback buffer.
|
||||
* @since 1.1
|
||||
|
@ -95,6 +98,13 @@ public class PushbackInputStream extends FilterInputStream {
|
|||
}
|
||||
this.buf = new byte[size];
|
||||
this.pos = size;
|
||||
|
||||
// use monitors when PushbackInputStream is sub-classed
|
||||
if (getClass() == PushbackInputStream.class) {
|
||||
closeLock = InternalLock.newLockOrNull();
|
||||
} else {
|
||||
closeLock = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -373,11 +383,26 @@ public class PushbackInputStream extends FilterInputStream {
|
|||
*
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public synchronized void close() throws IOException {
|
||||
if (in == null)
|
||||
return;
|
||||
in.close();
|
||||
in = null;
|
||||
buf = null;
|
||||
public void close() throws IOException {
|
||||
if (closeLock != null) {
|
||||
closeLock.lock();
|
||||
try {
|
||||
implClose();
|
||||
} finally {
|
||||
closeLock.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (this) {
|
||||
implClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void implClose() throws IOException {
|
||||
if (in != null) {
|
||||
in.close();
|
||||
in = null;
|
||||
buf = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1994, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1994, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -29,6 +29,7 @@ import java.nio.channels.FileChannel;
|
|||
|
||||
import jdk.internal.access.JavaIORandomAccessFileAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.Blocker;
|
||||
import sun.nio.ch.FileChannelImpl;
|
||||
|
||||
|
||||
|
@ -339,9 +340,13 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
|
|||
* @param mode the mode flags, a combination of the O_ constants
|
||||
* defined above
|
||||
*/
|
||||
private void open(String name, int mode)
|
||||
throws FileNotFoundException {
|
||||
open0(name, mode);
|
||||
private void open(String name, int mode) throws FileNotFoundException {
|
||||
long comp = Blocker.begin();
|
||||
try {
|
||||
open0(name, mode);
|
||||
} finally {
|
||||
Blocker.end(comp);
|
||||
}
|
||||
}
|
||||
|
||||
// 'Read' primitives
|
||||
|
@ -362,7 +367,12 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
|
|||
* end-of-file has been reached.
|
||||
*/
|
||||
public int read() throws IOException {
|
||||
return read0();
|
||||
long comp = Blocker.begin();
|
||||
try {
|
||||
return read0();
|
||||
} finally {
|
||||
Blocker.end(comp);
|
||||
}
|
||||
}
|
||||
|
||||
private native int read0() throws IOException;
|
||||
|
@ -374,7 +384,16 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
|
|||
* @param len the number of bytes to read.
|
||||
* @throws IOException If an I/O error has occurred.
|
||||
*/
|
||||
private native int readBytes(byte[] b, int off, int len) throws IOException;
|
||||
private int readBytes(byte[] b, int off, int len) throws IOException {
|
||||
long comp = Blocker.begin();
|
||||
try {
|
||||
return readBytes0(b, off, len);
|
||||
} finally {
|
||||
Blocker.end(comp);
|
||||
}
|
||||
}
|
||||
|
||||
private native int readBytes0(byte[] b, int off, int len) throws IOException;
|
||||
|
||||
/**
|
||||
* Reads up to {@code len} bytes of data from this file into an
|
||||
|
@ -519,7 +538,12 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
|
|||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public void write(int b) throws IOException {
|
||||
write0(b);
|
||||
long comp = Blocker.begin();
|
||||
try {
|
||||
write0(b);
|
||||
} finally {
|
||||
Blocker.end(comp);
|
||||
}
|
||||
}
|
||||
|
||||
private native void write0(int b) throws IOException;
|
||||
|
@ -532,7 +556,16 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
|
|||
* @param len the number of bytes that are written
|
||||
* @throws IOException If an I/O error has occurred.
|
||||
*/
|
||||
private native void writeBytes(byte[] b, int off, int len) throws IOException;
|
||||
private void writeBytes(byte[] b, int off, int len) throws IOException {
|
||||
long comp = Blocker.begin();
|
||||
try {
|
||||
writeBytes0(b, off, len);
|
||||
} finally {
|
||||
Blocker.end(comp);
|
||||
}
|
||||
}
|
||||
|
||||
private native void writeBytes0(byte[] b, int off, int len) throws IOException;
|
||||
|
||||
/**
|
||||
* Writes {@code b.length} bytes from the specified byte array
|
||||
|
@ -587,8 +620,12 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
|
|||
public void seek(long pos) throws IOException {
|
||||
if (pos < 0) {
|
||||
throw new IOException("Negative seek offset");
|
||||
} else {
|
||||
}
|
||||
long comp = Blocker.begin();
|
||||
try {
|
||||
seek0(pos);
|
||||
} finally {
|
||||
Blocker.end(comp);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -600,7 +637,16 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
|
|||
* @return the length of this file, measured in bytes.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
public native long length() throws IOException;
|
||||
public long length() throws IOException {
|
||||
long comp = Blocker.begin();
|
||||
try {
|
||||
return length0();
|
||||
} finally {
|
||||
Blocker.end(comp);
|
||||
}
|
||||
}
|
||||
|
||||
private native long length0() throws IOException;
|
||||
|
||||
/**
|
||||
* Sets the length of this file.
|
||||
|
@ -621,7 +667,16 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
|
|||
* @throws IOException If an I/O error occurs
|
||||
* @since 1.2
|
||||
*/
|
||||
public native void setLength(long newLength) throws IOException;
|
||||
public void setLength(long newLength) throws IOException {
|
||||
long comp = Blocker.begin();
|
||||
try {
|
||||
setLength0(newLength);
|
||||
} finally {
|
||||
Blocker.end(comp);
|
||||
}
|
||||
}
|
||||
|
||||
private native void setLength0(long newLength) throws IOException;
|
||||
|
||||
/**
|
||||
* Closes this random access file stream and releases any system
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -25,10 +25,10 @@
|
|||
|
||||
package java.io;
|
||||
|
||||
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.ReadOnlyBufferException;
|
||||
import java.util.Objects;
|
||||
import jdk.internal.misc.InternalLock;
|
||||
|
||||
/**
|
||||
* Abstract class for reading character streams. The only methods that a
|
||||
|
@ -170,6 +170,21 @@ public abstract class Reader implements Readable, Closeable {
|
|||
this.lock = lock;
|
||||
}
|
||||
|
||||
/**
|
||||
* For use by BufferedReader to create a character-stream reader that uses an
|
||||
* internal lock when BufferedReader is not extended and the given reader is
|
||||
* trusted, otherwise critical sections will synchronize on the given reader.
|
||||
*/
|
||||
Reader(Reader in) {
|
||||
Class<?> clazz = in.getClass();
|
||||
if (getClass() == BufferedReader.class &&
|
||||
(clazz == InputStreamReader.class || clazz == FileReader.class)) {
|
||||
this.lock = InternalLock.newLockOr(in);
|
||||
} else {
|
||||
this.lock = in;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to read characters into the specified character buffer.
|
||||
* The buffer is used as a repository of characters as-is: the only
|
||||
|
@ -297,21 +312,35 @@ public abstract class Reader implements Readable, Closeable {
|
|||
public long skip(long n) throws IOException {
|
||||
if (n < 0L)
|
||||
throw new IllegalArgumentException("skip value is negative");
|
||||
int nn = (int) Math.min(n, maxSkipBufferSize);
|
||||
synchronized (lock) {
|
||||
if ((skipBuffer == null) || (skipBuffer.length < nn))
|
||||
skipBuffer = new char[nn];
|
||||
long r = n;
|
||||
while (r > 0) {
|
||||
int nc = read(skipBuffer, 0, (int)Math.min(r, nn));
|
||||
if (nc == -1)
|
||||
break;
|
||||
r -= nc;
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
return implSkip(n);
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
return implSkip(n);
|
||||
}
|
||||
return n - r;
|
||||
}
|
||||
}
|
||||
|
||||
private long implSkip(long n) throws IOException {
|
||||
int nn = (int) Math.min(n, maxSkipBufferSize);
|
||||
if ((skipBuffer == null) || (skipBuffer.length < nn))
|
||||
skipBuffer = new char[nn];
|
||||
long r = n;
|
||||
while (r > 0) {
|
||||
int nc = read(skipBuffer, 0, (int)Math.min(r, nn));
|
||||
if (nc == -1)
|
||||
break;
|
||||
r -= nc;
|
||||
}
|
||||
return n - r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether this stream is ready to be read.
|
||||
*
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -25,8 +25,8 @@
|
|||
|
||||
package java.io;
|
||||
|
||||
|
||||
import java.util.Objects;
|
||||
import jdk.internal.misc.InternalLock;
|
||||
|
||||
/**
|
||||
* Abstract class for writing to character streams. The only methods that a
|
||||
|
@ -162,6 +162,21 @@ public abstract class Writer implements Appendable, Closeable, Flushable {
|
|||
this.lock = this;
|
||||
}
|
||||
|
||||
/**
|
||||
* For use by BufferedWriter to create a character-stream writer that uses an
|
||||
* internal lock when BufferedWriter is not extended and the given writer is
|
||||
* trusted, otherwise critical sections will synchronize on the given writer.
|
||||
*/
|
||||
Writer(Writer writer) {
|
||||
Class<?> clazz = writer.getClass();
|
||||
if (getClass() == BufferedWriter.class &&
|
||||
(clazz == OutputStreamWriter.class || clazz == FileWriter.class)) {
|
||||
this.lock = InternalLock.newLockOr(writer);
|
||||
} else {
|
||||
this.lock = writer;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new character-stream writer whose critical sections will
|
||||
* synchronize on the given object.
|
||||
|
@ -191,15 +206,29 @@ public abstract class Writer implements Appendable, Closeable, Flushable {
|
|||
* If an I/O error occurs
|
||||
*/
|
||||
public void write(int c) throws IOException {
|
||||
synchronized (lock) {
|
||||
if (writeBuffer == null){
|
||||
writeBuffer = new char[WRITE_BUFFER_SIZE];
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
implWrite(c);
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
implWrite(c);
|
||||
}
|
||||
writeBuffer[0] = (char) c;
|
||||
write(writeBuffer, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private void implWrite(int c) throws IOException {
|
||||
if (writeBuffer == null){
|
||||
writeBuffer = new char[WRITE_BUFFER_SIZE];
|
||||
}
|
||||
writeBuffer[0] = (char) c;
|
||||
write(writeBuffer, 0, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an array of characters.
|
||||
*
|
||||
|
@ -276,21 +305,35 @@ public abstract class Writer implements Appendable, Closeable, Flushable {
|
|||
* If an I/O error occurs
|
||||
*/
|
||||
public void write(String str, int off, int len) throws IOException {
|
||||
synchronized (lock) {
|
||||
char cbuf[];
|
||||
if (len <= WRITE_BUFFER_SIZE) {
|
||||
if (writeBuffer == null) {
|
||||
writeBuffer = new char[WRITE_BUFFER_SIZE];
|
||||
}
|
||||
cbuf = writeBuffer;
|
||||
} else { // Don't permanently allocate very large buffers.
|
||||
cbuf = new char[len];
|
||||
Object lock = this.lock;
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
implWrite(str, off, len);
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else {
|
||||
synchronized (lock) {
|
||||
implWrite(str, off, len);
|
||||
}
|
||||
str.getChars(off, (off + len), cbuf, 0);
|
||||
write(cbuf, 0, len);
|
||||
}
|
||||
}
|
||||
|
||||
private void implWrite(String str, int off, int len) throws IOException {
|
||||
char cbuf[];
|
||||
if (len <= WRITE_BUFFER_SIZE) {
|
||||
if (writeBuffer == null) {
|
||||
writeBuffer = new char[WRITE_BUFFER_SIZE];
|
||||
}
|
||||
cbuf = writeBuffer;
|
||||
} else { // Don't permanently allocate very large buffers.
|
||||
cbuf = new char[len];
|
||||
}
|
||||
str.getChars(off, (off + len), cbuf, 0);
|
||||
write(cbuf, 0, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the specified character sequence to this writer.
|
||||
*
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -25,6 +25,7 @@
|
|||
package java.lang;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
|
||||
/*
|
||||
* Class to track and run user level shutdown hooks registered through
|
||||
|
@ -97,9 +98,15 @@ class ApplicationShutdownHooks {
|
|||
threads = hooks.keySet();
|
||||
hooks = null;
|
||||
}
|
||||
|
||||
for (Thread hook : threads) {
|
||||
hook.start();
|
||||
try {
|
||||
hook.start();
|
||||
} catch (IllegalThreadStateException ignore) {
|
||||
// already started
|
||||
} catch (RejectedExecutionException ignore) {
|
||||
// scheduler shutdown?
|
||||
assert hook.isVirtual();
|
||||
}
|
||||
}
|
||||
for (Thread hook : threads) {
|
||||
while (true) {
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
*/
|
||||
|
||||
package java.lang;
|
||||
import java.lang.ref.*;
|
||||
|
||||
/**
|
||||
* This class extends {@code ThreadLocal} to provide inheritance of values
|
||||
|
@ -49,6 +48,7 @@ import java.lang.ref.*;
|
|||
* @author Josh Bloch and Doug Lea
|
||||
* @see ThreadLocal
|
||||
* @since 1.2
|
||||
* @see Thread.Builder#inheritInheritableThreadLocals(boolean)
|
||||
*/
|
||||
|
||||
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
|
||||
|
@ -78,8 +78,9 @@ public class InheritableThreadLocal<T> extends ThreadLocal<T> {
|
|||
*
|
||||
* @param t the current thread
|
||||
*/
|
||||
@Override
|
||||
ThreadLocalMap getMap(Thread t) {
|
||||
return t.inheritableThreadLocals;
|
||||
return t.inheritableThreadLocals;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -88,6 +89,7 @@ public class InheritableThreadLocal<T> extends ThreadLocal<T> {
|
|||
* @param t the current thread
|
||||
* @param firstValue value for the initial entry of the table.
|
||||
*/
|
||||
@Override
|
||||
void createMap(Thread t, T firstValue) {
|
||||
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -27,6 +27,8 @@ package java.lang;
|
|||
import java.lang.StackWalker.StackFrame;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
import jdk.internal.vm.Continuation;
|
||||
import jdk.internal.vm.ContinuationScope;
|
||||
|
||||
import static java.lang.StackWalker.ExtendedOption.LOCALS_AND_OPERANDS;
|
||||
|
||||
|
@ -177,11 +179,74 @@ interface LiveStackFrame extends StackFrame {
|
|||
* and it denies access to {@code RuntimePermission("getStackWalkerWithClassReference")}.
|
||||
*/
|
||||
public static StackWalker getStackWalker(Set<StackWalker.Option> options) {
|
||||
return getStackWalker(options, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a {@code StackWalker} instance with the given options specifying
|
||||
* the stack frame information it can access, and which will traverse at most
|
||||
* the given {@code maxDepth} number of stack frames. If no option is
|
||||
* specified, this {@code StackWalker} obtains the method name and
|
||||
* the class name with all
|
||||
* {@linkplain StackWalker.Option#SHOW_HIDDEN_FRAMES hidden frames} skipped.
|
||||
* The returned {@code StackWalker} can get locals and operands.
|
||||
*
|
||||
* @param options stack walk {@link StackWalker.Option options}
|
||||
* @param contScope the continuation scope up to which (inclusive) to walk the stack
|
||||
*
|
||||
* @throws SecurityException if the security manager is present and
|
||||
* it denies access to {@code RuntimePermission("liveStackFrames")}; or
|
||||
* or if the given {@code options} contains
|
||||
* {@link StackWalker.Option#RETAIN_CLASS_REFERENCE Option.RETAIN_CLASS_REFERENCE}
|
||||
* and it denies access to {@code RuntimePermission("getStackWalkerWithClassReference")}.
|
||||
*/
|
||||
public static StackWalker getStackWalker(Set<StackWalker.Option> options, ContinuationScope contScope) {
|
||||
@SuppressWarnings("removal")
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
sm.checkPermission(new RuntimePermission("liveStackFrames"));
|
||||
}
|
||||
return StackWalker.newInstance(options, LOCALS_AND_OPERANDS);
|
||||
return StackWalker.newInstance(options, LOCALS_AND_OPERANDS, contScope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets {@code StackWalker} of the given unmounted continuation, that can get locals and operands.
|
||||
*
|
||||
* @param continuation the continuation to walk
|
||||
*
|
||||
* @throws SecurityException if the security manager is present and
|
||||
* denies access to {@code RuntimePermission("liveStackFrames")}
|
||||
*/
|
||||
public static StackWalker getStackWalker(Continuation continuation) {
|
||||
return getStackWalker(EnumSet.noneOf(StackWalker.Option.class), continuation.getScope(), continuation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a {@code StackWalker} instance with the given options specifying
|
||||
* the stack frame information it can access, and which will traverse at most
|
||||
* the given {@code maxDepth} number of stack frames. If no option is
|
||||
* specified, this {@code StackWalker} obtains the method name and
|
||||
* the class name with all
|
||||
* {@linkplain StackWalker.Option#SHOW_HIDDEN_FRAMES hidden frames} skipped.
|
||||
* The returned {@code StackWalker} can get locals and operands.
|
||||
*
|
||||
* @param options stack walk {@link StackWalker.Option options}
|
||||
* @param continuation the continuation to walk
|
||||
*
|
||||
* @throws SecurityException if the security manager is present and
|
||||
* it denies access to {@code RuntimePermission("liveStackFrames")}; or
|
||||
* or if the given {@code options} contains
|
||||
* {@link StackWalker.Option#RETAIN_CLASS_REFERENCE Option.RETAIN_CLASS_REFERENCE}
|
||||
* and it denies access to {@code RuntimePermission("getStackWalkerWithClassReference")}.
|
||||
*/
|
||||
public static StackWalker getStackWalker(Set<StackWalker.Option> options,
|
||||
ContinuationScope contScope,
|
||||
Continuation continuation) {
|
||||
@SuppressWarnings("removal")
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
sm.checkPermission(new RuntimePermission("liveStackFrames"));
|
||||
}
|
||||
return StackWalker.newInstance(options, LOCALS_AND_OPERANDS, contScope, continuation);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1994, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1994, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -25,6 +25,7 @@
|
|||
|
||||
package java.lang;
|
||||
|
||||
import jdk.internal.misc.Blocker;
|
||||
import jdk.internal.vm.annotation.IntrinsicCandidate;
|
||||
|
||||
/**
|
||||
|
@ -79,11 +80,11 @@ public class Object {
|
|||
* This integer need not remain consistent from one execution of an
|
||||
* application to another execution of the same application.
|
||||
* <li>If two objects are equal according to the {@link
|
||||
* equals(Object) equals} method, then calling the {@code
|
||||
* #equals(Object) equals} method, then calling the {@code
|
||||
* hashCode} method on each of the two objects must produce the
|
||||
* same integer result.
|
||||
* <li>It is <em>not</em> required that if two objects are unequal
|
||||
* according to the {@link equals(Object) equals} method, then
|
||||
* according to the {@link #equals(Object) equals} method, then
|
||||
* calling the {@code hashCode} method on each of the two objects
|
||||
* must produce distinct integer results. However, the programmer
|
||||
* should be aware that producing distinct integer results for
|
||||
|
@ -148,7 +149,7 @@ public class Object {
|
|||
* relation, each equivalence class only has a single element.
|
||||
*
|
||||
* @apiNote
|
||||
* It is generally necessary to override the {@link hashCode hashCode}
|
||||
* It is generally necessary to override the {@link #hashCode() hashCode}
|
||||
* method whenever this method is overridden, so as to maintain the
|
||||
* general contract for the {@code hashCode} method, which states
|
||||
* that equal objects must have equal hash codes.
|
||||
|
@ -359,7 +360,22 @@ public class Object {
|
|||
* @see #wait()
|
||||
* @see #wait(long, int)
|
||||
*/
|
||||
public final native void wait(long timeoutMillis) throws InterruptedException;
|
||||
public final void wait(long timeoutMillis) throws InterruptedException {
|
||||
long comp = Blocker.begin();
|
||||
try {
|
||||
wait0(timeoutMillis);
|
||||
} catch (InterruptedException e) {
|
||||
Thread thread = Thread.currentThread();
|
||||
if (thread.isVirtual())
|
||||
thread.getAndClearInterrupt();
|
||||
throw e;
|
||||
} finally {
|
||||
Blocker.end(comp);
|
||||
}
|
||||
}
|
||||
|
||||
// final modifier so method not in vtable
|
||||
private final native void wait0(long timeoutMillis) throws InterruptedException;
|
||||
|
||||
/**
|
||||
* Causes the current thread to wait until it is awakened, typically
|
||||
|
|
132
src/java.base/share/classes/java/lang/PinnedThreadPrinter.java
Normal file
132
src/java.base/share/classes/java/lang/PinnedThreadPrinter.java
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package java.lang;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import static java.lang.StackWalker.Option.*;
|
||||
|
||||
/**
|
||||
* Helper class to print the virtual thread stack trace when pinned.
|
||||
*
|
||||
* The class maintains a ClassValue with the hashes of stack traces that are pinned by
|
||||
* code in that Class. This is used to avoid printing the same stack trace many times.
|
||||
*/
|
||||
class PinnedThreadPrinter {
|
||||
static final StackWalker STACK_WALKER;
|
||||
static {
|
||||
var options = Set.of(SHOW_REFLECT_FRAMES, RETAIN_CLASS_REFERENCE);
|
||||
PrivilegedAction<StackWalker> pa = () ->
|
||||
LiveStackFrame.getStackWalker(options, VirtualThread.continuationScope());
|
||||
@SuppressWarnings("removal")
|
||||
var stackWalker = AccessController.doPrivileged(pa);
|
||||
STACK_WALKER = stackWalker;
|
||||
}
|
||||
|
||||
private static final ClassValue<Hashes> HASHES = new ClassValue<>() {
|
||||
@Override
|
||||
protected Hashes computeValue(Class<?> type) {
|
||||
return new Hashes();
|
||||
}
|
||||
};
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static class Hashes extends LinkedHashMap<Integer, Boolean> {
|
||||
boolean add(int hash) {
|
||||
return (putIfAbsent(hash, Boolean.TRUE) == null);
|
||||
}
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<Integer, Boolean> oldest) {
|
||||
// limit number of hashes
|
||||
return size() > 8;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hash of the given stack trace. The hash is based on the class,
|
||||
* method and bytecode index.
|
||||
*/
|
||||
private static int hash(List<LiveStackFrame> stack) {
|
||||
int hash = 0;
|
||||
for (LiveStackFrame frame : stack) {
|
||||
hash = (31 * hash) + Objects.hash(frame.getDeclaringClass(),
|
||||
frame.getMethodName(),
|
||||
frame.getByteCodeIndex());
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the stack trace of the current mounted vthread continuation.
|
||||
*
|
||||
* @param printAll true to print all stack frames, false to only print the
|
||||
* frames that are native or holding a monitor
|
||||
*/
|
||||
static void printStackTrace(PrintStream out, boolean printAll) {
|
||||
List<LiveStackFrame> stack = STACK_WALKER.walk(s ->
|
||||
s.map(f -> (LiveStackFrame) f)
|
||||
.filter(f -> f.getDeclaringClass() != PinnedThreadPrinter.class)
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
|
||||
// find the closest frame that is causing the thread to be pinned
|
||||
stack.stream()
|
||||
.filter(f -> (f.isNativeMethod() || f.getMonitors().length > 0))
|
||||
.map(LiveStackFrame::getDeclaringClass)
|
||||
.findFirst()
|
||||
.ifPresent(klass -> {
|
||||
int hash = hash(stack);
|
||||
Hashes hashes = HASHES.get(klass);
|
||||
synchronized (hashes) {
|
||||
// print the stack trace if not already seen
|
||||
if (hashes.add(hash)) {
|
||||
printStackTrace(stack, out, printAll);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void printStackTrace(List<LiveStackFrame> stack,
|
||||
PrintStream out,
|
||||
boolean printAll) {
|
||||
out.println(Thread.currentThread());
|
||||
for (LiveStackFrame frame : stack) {
|
||||
var ste = frame.toStackTraceElement();
|
||||
int monitorCount = frame.getMonitors().length;
|
||||
if (monitorCount > 0 || frame.isNativeMethod()) {
|
||||
out.format(" %s <== monitors:%d%n", ste, monitorCount);
|
||||
} else if (printAll) {
|
||||
out.format(" %s%n", ste);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1994, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -26,44 +26,19 @@
|
|||
package java.lang;
|
||||
|
||||
/**
|
||||
* The {@code Runnable} interface should be implemented by any
|
||||
* class whose instances are intended to be executed by a thread. The
|
||||
* class must define a method of no arguments called {@code run}.
|
||||
* <p>
|
||||
* This interface is designed to provide a common protocol for objects that
|
||||
* wish to execute code while they are active. For example,
|
||||
* {@code Runnable} is implemented by class {@code Thread}.
|
||||
* Being active simply means that a thread has been started and has not
|
||||
* yet been stopped.
|
||||
* <p>
|
||||
* In addition, {@code Runnable} provides the means for a class to be
|
||||
* active while not subclassing {@code Thread}. A class that implements
|
||||
* {@code Runnable} can run without subclassing {@code Thread}
|
||||
* by instantiating a {@code Thread} instance and passing itself in
|
||||
* as the target. In most cases, the {@code Runnable} interface should
|
||||
* be used if you are only planning to override the {@code run()}
|
||||
* method and no other {@code Thread} methods.
|
||||
* This is important because classes should not be subclassed
|
||||
* unless the programmer intends on modifying or enhancing the fundamental
|
||||
* behavior of the class.
|
||||
* Represents an operation that does not return a result.
|
||||
*
|
||||
* <p> This is a {@linkplain java.util.function functional interface}
|
||||
* whose functional method is {@link #run()}.
|
||||
*
|
||||
* @author Arthur van Hoff
|
||||
* @see java.lang.Thread
|
||||
* @see java.util.concurrent.Callable
|
||||
* @since 1.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Runnable {
|
||||
/**
|
||||
* When an object implementing interface {@code Runnable} is used
|
||||
* to create a thread, starting the thread causes the object's
|
||||
* {@code run} method to be called in that separately executing
|
||||
* thread.
|
||||
* <p>
|
||||
* The general contract of the method {@code run} is that it may
|
||||
* take any action whatsoever.
|
||||
*
|
||||
* @see java.lang.Thread#run()
|
||||
* Runs this operation.
|
||||
*/
|
||||
public abstract void run();
|
||||
void run();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -26,6 +26,7 @@ package java.lang;
|
|||
|
||||
import jdk.internal.access.JavaLangInvokeAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.vm.ContinuationScope;
|
||||
|
||||
import java.lang.StackWalker.StackFrame;
|
||||
import java.lang.invoke.MethodType;
|
||||
|
@ -35,8 +36,9 @@ class StackFrameInfo implements StackFrame {
|
|||
SharedSecrets.getJavaLangInvokeAccess();
|
||||
|
||||
private final boolean retainClassRef;
|
||||
private final Object memberName; // MemberName initialized by VM
|
||||
private int bci; // initialized by VM to >= 0
|
||||
private Object memberName; // MemberName initialized by VM
|
||||
private int bci; // initialized by VM to >= 0
|
||||
private ContinuationScope contScope;
|
||||
private volatile StackTraceElement ste;
|
||||
|
||||
/*
|
||||
|
@ -115,6 +117,10 @@ class StackFrameInfo implements StackFrame {
|
|||
return JLIA.isNative(memberName);
|
||||
}
|
||||
|
||||
private String getContinuationScopeName() {
|
||||
return contScope != null ? contScope.getName() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toStackTraceElement().toString();
|
||||
|
|
|
@ -41,6 +41,8 @@ import java.util.function.Consumer;
|
|||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
import jdk.internal.vm.Continuation;
|
||||
import jdk.internal.vm.ContinuationScope;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
|
||||
import static java.lang.StackStreamFactory.WalkerState.*;
|
||||
|
@ -126,6 +128,8 @@ final class StackStreamFactory {
|
|||
protected int depth; // traversed stack depth
|
||||
protected FrameBuffer<? extends T> frameBuffer;
|
||||
protected long anchor;
|
||||
protected final ContinuationScope contScope;
|
||||
protected Continuation continuation;
|
||||
|
||||
// buffers to fill in stack frame information
|
||||
protected AbstractStackWalker(StackWalker walker, int mode) {
|
||||
|
@ -137,6 +141,14 @@ final class StackStreamFactory {
|
|||
this.walker = walker;
|
||||
this.maxDepth = maxDepth;
|
||||
this.depth = 0;
|
||||
ContinuationScope scope = walker.getContScope();
|
||||
if (scope == null && thread.isVirtual()) {
|
||||
this.contScope = VirtualThread.continuationScope();
|
||||
this.continuation = null;
|
||||
} else {
|
||||
this.contScope = scope;
|
||||
this.continuation = walker.getContinuation();
|
||||
}
|
||||
}
|
||||
|
||||
private int toStackWalkMode(StackWalker walker, int mode) {
|
||||
|
@ -236,6 +248,12 @@ final class StackStreamFactory {
|
|||
*/
|
||||
final R walk() {
|
||||
checkState(NEW);
|
||||
return (continuation != null)
|
||||
? Continuation.wrapWalk(continuation, contScope, this::walkHelper)
|
||||
: walkHelper();
|
||||
}
|
||||
|
||||
private final R walkHelper() {
|
||||
try {
|
||||
// VM will need to stabilize the stack before walking. It will invoke
|
||||
// the AbstractStackWalker::doStackWalk method once it fetches the first batch.
|
||||
|
@ -311,7 +329,10 @@ final class StackStreamFactory {
|
|||
*/
|
||||
private int getNextBatch() {
|
||||
int nextBatchSize = Math.min(maxDepth - depth, getNextBatchSize());
|
||||
if (!frameBuffer.isActive() || nextBatchSize <= 0) {
|
||||
|
||||
if (!frameBuffer.isActive()
|
||||
|| (nextBatchSize <= 0)
|
||||
|| (frameBuffer.isAtBottom() && !hasMoreContinuations())) {
|
||||
if (isDebug) {
|
||||
System.out.format(" more stack walk done%n");
|
||||
}
|
||||
|
@ -319,7 +340,26 @@ final class StackStreamFactory {
|
|||
return 0;
|
||||
}
|
||||
|
||||
return fetchStackFrames(nextBatchSize);
|
||||
if (frameBuffer.isAtBottom() && hasMoreContinuations()) {
|
||||
setContinuation(continuation.getParent());
|
||||
}
|
||||
|
||||
int numFrames = fetchStackFrames(nextBatchSize);
|
||||
if (numFrames == 0 && !hasMoreContinuations()) {
|
||||
frameBuffer.freeze(); // done stack walking
|
||||
}
|
||||
return numFrames;
|
||||
}
|
||||
|
||||
private boolean hasMoreContinuations() {
|
||||
return (continuation != null)
|
||||
&& (continuation.getScope() != contScope)
|
||||
&& (continuation.getParent() != null);
|
||||
}
|
||||
|
||||
private void setContinuation(Continuation cont) {
|
||||
this.continuation = cont;
|
||||
setContinuation(anchor, frameBuffer.frames(), cont);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -368,6 +408,7 @@ final class StackStreamFactory {
|
|||
initFrameBuffer();
|
||||
|
||||
return callStackWalk(mode, 0,
|
||||
contScope, continuation,
|
||||
frameBuffer.curBatchFrameCount(),
|
||||
frameBuffer.startIndex(),
|
||||
frameBuffer.frames());
|
||||
|
@ -390,10 +431,10 @@ final class StackStreamFactory {
|
|||
System.out.format(" more stack walk requesting %d got %d to %d frames%n",
|
||||
batchSize, frameBuffer.startIndex(), endIndex);
|
||||
}
|
||||
|
||||
int numFrames = endIndex - startIndex;
|
||||
if (numFrames == 0) {
|
||||
frameBuffer.freeze(); // done stack walking
|
||||
} else {
|
||||
|
||||
if (numFrames > 0) {
|
||||
frameBuffer.setBatch(depth, startIndex, endIndex);
|
||||
}
|
||||
return numFrames;
|
||||
|
@ -405,6 +446,8 @@ final class StackStreamFactory {
|
|||
*
|
||||
* @param mode mode of stack walking
|
||||
* @param skipframes number of frames to be skipped before filling the frame buffer.
|
||||
* @param contScope the continuation scope to walk.
|
||||
* @param continuation the continuation to walk, or {@code null} if walking a thread.
|
||||
* @param batchSize the batch size, max. number of elements to be filled in the frame buffers.
|
||||
* @param startIndex start index of the frame buffers to be filled.
|
||||
* @param frames Either a Class<?> array, if mode is {@link #FILL_CLASS_REFS_ONLY}
|
||||
|
@ -412,6 +455,7 @@ final class StackStreamFactory {
|
|||
* @return Result of AbstractStackWalker::doStackWalk
|
||||
*/
|
||||
private native R callStackWalk(long mode, int skipframes,
|
||||
ContinuationScope contScope, Continuation continuation,
|
||||
int batchSize, int startIndex,
|
||||
T[] frames);
|
||||
|
||||
|
@ -430,6 +474,8 @@ final class StackStreamFactory {
|
|||
private native int fetchStackFrames(long mode, long anchor,
|
||||
int batchSize, int startIndex,
|
||||
T[] frames);
|
||||
|
||||
private native void setContinuation(long anchor, T[] frames, Continuation cont);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -497,6 +543,12 @@ final class StackStreamFactory {
|
|||
final Class<?> at(int index) {
|
||||
return stackFrames[index].declaringClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
final boolean filter(int index) {
|
||||
return stackFrames[index].declaringClass() == Continuation.class
|
||||
&& "yield0".equals(stackFrames[index].getMethodName());
|
||||
}
|
||||
}
|
||||
|
||||
final Function<? super Stream<StackFrame>, ? extends T> function; // callback
|
||||
|
@ -633,6 +685,9 @@ final class StackStreamFactory {
|
|||
@Override
|
||||
final Class<?> at(int index) { return classes[index];}
|
||||
|
||||
@Override
|
||||
final boolean filter(int index) { return false; }
|
||||
|
||||
|
||||
// ------ subclass may override the following methods -------
|
||||
/**
|
||||
|
@ -765,6 +820,12 @@ final class StackStreamFactory {
|
|||
final Class<?> at(int index) {
|
||||
return stackFrames[index].declaringClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
final boolean filter(int index) {
|
||||
return stackFrames[index].declaringClass() == Continuation.class
|
||||
&& "yield0".equals(stackFrames[index].getMethodName());
|
||||
}
|
||||
}
|
||||
|
||||
LiveStackInfoTraverser(StackWalker walker,
|
||||
|
@ -834,6 +895,13 @@ final class StackStreamFactory {
|
|||
*/
|
||||
abstract Class<?> at(int index);
|
||||
|
||||
/**
|
||||
* Filter out frames at the top of a batch
|
||||
* @param index the position of the frame.
|
||||
* @return true if the frame should be skipped
|
||||
*/
|
||||
abstract boolean filter(int index);
|
||||
|
||||
// ------ subclass may override the following methods -------
|
||||
|
||||
/*
|
||||
|
@ -881,7 +949,15 @@ final class StackStreamFactory {
|
|||
* it is done for traversal. All stack frames have been traversed.
|
||||
*/
|
||||
final boolean isActive() {
|
||||
return origin > 0 && (fence == 0 || origin < fence || fence == currentBatchSize);
|
||||
return origin > 0; // && (fence == 0 || origin < fence || fence == currentBatchSize);
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests if this frame buffer is at the end of the stack
|
||||
* and all frames have been traversed.
|
||||
*/
|
||||
final boolean isAtBottom() {
|
||||
return origin > 0 && origin >= fence && fence < currentBatchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -929,16 +1005,13 @@ final class StackStreamFactory {
|
|||
|
||||
this.origin = startIndex;
|
||||
this.fence = endIndex;
|
||||
if (depth == 0 && fence > 0) {
|
||||
// filter the frames due to the stack stream implementation
|
||||
for (int i = START_POS; i < fence; i++) {
|
||||
Class<?> c = at(i);
|
||||
if (isDebug) System.err.format(" frame %d: %s%n", i, c);
|
||||
if (filterStackWalkImpl(c)) {
|
||||
origin++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
for (int i = START_POS; i < fence; i++) {
|
||||
if (isDebug) System.err.format(" frame %d: %s%n", i, at(i));
|
||||
if ((depth == 0 && filterStackWalkImpl(at(i))) // filter the frames due to the stack stream implementation
|
||||
|| filter(i)) {
|
||||
origin++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -529,22 +529,17 @@ public final class StackTraceElement implements java.io.Serializable {
|
|||
|
||||
/*
|
||||
* Returns an array of StackTraceElements of the given depth
|
||||
* filled from the backtrace of a given Throwable.
|
||||
* filled from the given backtrace.
|
||||
*/
|
||||
static StackTraceElement[] of(Throwable x, int depth) {
|
||||
static StackTraceElement[] of(Object x, int depth) {
|
||||
StackTraceElement[] stackTrace = new StackTraceElement[depth];
|
||||
for (int i = 0; i < depth; i++) {
|
||||
stackTrace[i] = new StackTraceElement();
|
||||
}
|
||||
|
||||
// VM to fill in StackTraceElement
|
||||
initStackTraceElements(stackTrace, x);
|
||||
|
||||
// ensure the proper StackTraceElement initialization
|
||||
for (StackTraceElement ste : stackTrace) {
|
||||
ste.computeFormat();
|
||||
}
|
||||
return stackTrace;
|
||||
initStackTraceElements(stackTrace, x, depth);
|
||||
return of(stackTrace);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -558,12 +553,20 @@ public final class StackTraceElement implements java.io.Serializable {
|
|||
return ste;
|
||||
}
|
||||
|
||||
static StackTraceElement[] of(StackTraceElement[] stackTrace) {
|
||||
// ensure the proper StackTraceElement initialization
|
||||
for (StackTraceElement ste : stackTrace) {
|
||||
ste.computeFormat();
|
||||
}
|
||||
return stackTrace;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the given stack trace elements with the backtrace
|
||||
* of the given Throwable.
|
||||
*/
|
||||
private static native void initStackTraceElements(StackTraceElement[] elements,
|
||||
Throwable x);
|
||||
Object x, int depth);
|
||||
/*
|
||||
* Sets the given stack trace element with the given StackFrameInfo
|
||||
*/
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -33,6 +33,8 @@ import java.util.Set;
|
|||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.internal.vm.Continuation;
|
||||
import jdk.internal.vm.ContinuationScope;
|
||||
|
||||
/**
|
||||
* A stack walker.
|
||||
|
@ -293,6 +295,8 @@ public final class StackWalker {
|
|||
private static final StackWalker DEFAULT_WALKER =
|
||||
new StackWalker(DEFAULT_EMPTY_OPTION);
|
||||
|
||||
private final Continuation continuation;
|
||||
private final ContinuationScope contScope;
|
||||
private final Set<Option> options;
|
||||
private final ExtendedOption extendedOption;
|
||||
private final int estimateDepth;
|
||||
|
@ -315,6 +319,24 @@ public final class StackWalker {
|
|||
return DEFAULT_WALKER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code StackWalker} instance.
|
||||
*
|
||||
* <p> This {@code StackWalker} is configured to skip all
|
||||
* {@linkplain Option#SHOW_HIDDEN_FRAMES hidden frames} and
|
||||
* no {@linkplain Option#RETAIN_CLASS_REFERENCE class reference} is retained.
|
||||
*
|
||||
* @param contScope the continuation scope up to which (inclusive) to walk the stack
|
||||
*
|
||||
* @return a {@code StackWalker} configured to skip all
|
||||
* {@linkplain Option#SHOW_HIDDEN_FRAMES hidden frames} and
|
||||
* no {@linkplain Option#RETAIN_CLASS_REFERENCE class reference} is retained.
|
||||
*
|
||||
*/
|
||||
static StackWalker getInstance(ContinuationScope contScope) {
|
||||
return getInstance(EnumSet.noneOf(Option.class), contScope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code StackWalker} instance with the given option specifying
|
||||
* the stack frame information it can access.
|
||||
|
@ -336,6 +358,28 @@ public final class StackWalker {
|
|||
return getInstance(EnumSet.of(Objects.requireNonNull(option)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code StackWalker} instance with the given option specifying
|
||||
* the stack frame information it can access.
|
||||
*
|
||||
* <p>
|
||||
* If a security manager is present and the given {@code option} is
|
||||
* {@link Option#RETAIN_CLASS_REFERENCE Option.RETAIN_CLASS_REFERENCE},
|
||||
* it calls its {@link SecurityManager#checkPermission checkPermission}
|
||||
* method for {@code RuntimePermission("getStackWalkerWithClassReference")}.
|
||||
*
|
||||
* @param option {@link Option stack walking option}
|
||||
* @param contScope the continuation scope up to which (inclusive) to walk the stack
|
||||
*
|
||||
* @return a {@code StackWalker} configured with the given option
|
||||
*
|
||||
* @throws SecurityException if a security manager exists and its
|
||||
* {@code checkPermission} method denies access.
|
||||
*/
|
||||
static StackWalker getInstance(Option option, ContinuationScope contScope) {
|
||||
return getInstance(EnumSet.of(Objects.requireNonNull(option)), contScope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code StackWalker} instance with the given {@code options} specifying
|
||||
* the stack frame information it can access. If the given {@code options}
|
||||
|
@ -357,13 +401,38 @@ public final class StackWalker {
|
|||
* {@code checkPermission} method denies access.
|
||||
*/
|
||||
public static StackWalker getInstance(Set<Option> options) {
|
||||
if (options.isEmpty()) {
|
||||
return getInstance(options, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code StackWalker} instance with the given {@code options} specifying
|
||||
* the stack frame information it can access. If the given {@code options}
|
||||
* is empty, this {@code StackWalker} is configured to skip all
|
||||
* {@linkplain Option#SHOW_HIDDEN_FRAMES hidden frames} and no
|
||||
* {@linkplain Option#RETAIN_CLASS_REFERENCE class reference} is retained.
|
||||
*
|
||||
* <p>
|
||||
* If a security manager is present and the given {@code options} contains
|
||||
* {@link Option#RETAIN_CLASS_REFERENCE Option.RETAIN_CLASS_REFERENCE},
|
||||
* it calls its {@link SecurityManager#checkPermission checkPermission}
|
||||
* method for {@code RuntimePermission("getStackWalkerWithClassReference")}.
|
||||
*
|
||||
* @param options {@link Option stack walking option}
|
||||
* @param contScope the continuation scope up to which (inclusive) to walk the stack
|
||||
*
|
||||
* @return a {@code StackWalker} configured with the given options
|
||||
*
|
||||
* @throws SecurityException if a security manager exists and its
|
||||
* {@code checkPermission} method denies access.
|
||||
*/
|
||||
static StackWalker getInstance(Set<Option> options, ContinuationScope contScope) {
|
||||
if (options.isEmpty() && contScope == null) {
|
||||
return DEFAULT_WALKER;
|
||||
}
|
||||
|
||||
EnumSet<Option> optionSet = toEnumSet(options);
|
||||
checkPermission(optionSet);
|
||||
return new StackWalker(optionSet);
|
||||
return new StackWalker(optionSet, contScope);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -404,16 +473,37 @@ public final class StackWalker {
|
|||
|
||||
// ----- private constructors ------
|
||||
private StackWalker(EnumSet<Option> options) {
|
||||
this(options, 0, null);
|
||||
this(options, 0, null, null, null);
|
||||
}
|
||||
private StackWalker(EnumSet<Option> options, ContinuationScope contScope) {
|
||||
this(options, 0, null, contScope, null);
|
||||
}
|
||||
private StackWalker(EnumSet<Option> options, ContinuationScope contScope, Continuation continuation) {
|
||||
this(options, 0, null, contScope, continuation);
|
||||
}
|
||||
private StackWalker(EnumSet<Option> options, int estimateDepth) {
|
||||
this(options, estimateDepth, null);
|
||||
this(options, estimateDepth, null, null, null);
|
||||
}
|
||||
private StackWalker(EnumSet<Option> options, int estimateDepth, ExtendedOption extendedOption) {
|
||||
private StackWalker(EnumSet<Option> options, int estimateDepth, ContinuationScope contScope) {
|
||||
this(options, estimateDepth, null, contScope, null);
|
||||
}
|
||||
private StackWalker(EnumSet<Option> options,
|
||||
int estimateDepth,
|
||||
ContinuationScope contScope,
|
||||
Continuation continuation) {
|
||||
this(options, estimateDepth, null, contScope, continuation);
|
||||
}
|
||||
private StackWalker(EnumSet<Option> options,
|
||||
int estimateDepth,
|
||||
ExtendedOption extendedOption,
|
||||
ContinuationScope contScope,
|
||||
Continuation continuation) {
|
||||
this.options = options;
|
||||
this.estimateDepth = estimateDepth;
|
||||
this.extendedOption = extendedOption;
|
||||
this.retainClassRef = hasOption(Option.RETAIN_CLASS_REFERENCE);
|
||||
this.contScope = contScope;
|
||||
this.continuation = continuation;
|
||||
}
|
||||
|
||||
private static void checkPermission(Set<Option> options) {
|
||||
|
@ -597,6 +687,9 @@ public final class StackWalker {
|
|||
throw new UnsupportedOperationException("This stack walker " +
|
||||
"does not have RETAIN_CLASS_REFERENCE access");
|
||||
}
|
||||
if (continuation != null) {
|
||||
throw new UnsupportedOperationException("This stack walker walks a continuation");
|
||||
}
|
||||
|
||||
return StackStreamFactory.makeCallerFinder(this).findCaller();
|
||||
}
|
||||
|
@ -604,9 +697,19 @@ public final class StackWalker {
|
|||
// ---- package access ----
|
||||
|
||||
static StackWalker newInstance(Set<Option> options, ExtendedOption extendedOption) {
|
||||
return newInstance(options, extendedOption, null);
|
||||
}
|
||||
|
||||
static StackWalker newInstance(Set<Option> options, ExtendedOption extendedOption, ContinuationScope contScope) {
|
||||
EnumSet<Option> optionSet = toEnumSet(options);
|
||||
checkPermission(optionSet);
|
||||
return new StackWalker(optionSet, 0, extendedOption);
|
||||
return new StackWalker(optionSet, 0, extendedOption, contScope, null);
|
||||
}
|
||||
|
||||
static StackWalker newInstance(Set<Option> options, ExtendedOption extendedOption, ContinuationScope contScope, Continuation continuation) {
|
||||
EnumSet<Option> optionSet = toEnumSet(options);
|
||||
checkPermission(optionSet);
|
||||
return new StackWalker(optionSet, 0, extendedOption, contScope, continuation);
|
||||
}
|
||||
|
||||
int estimateDepth() {
|
||||
|
@ -620,4 +723,12 @@ public final class StackWalker {
|
|||
boolean hasLocalsOperandsOption() {
|
||||
return extendedOption == ExtendedOption.LOCALS_AND_OPERANDS;
|
||||
}
|
||||
|
||||
ContinuationScope getContScope() {
|
||||
return contScope;
|
||||
}
|
||||
|
||||
Continuation getContinuation() {
|
||||
return continuation;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import java.io.FileInputStream;
|
|||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
|
@ -62,6 +63,7 @@ import java.util.PropertyPermission;
|
|||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Stream;
|
||||
|
@ -78,8 +80,13 @@ import jdk.internal.logger.LoggerFinderLoader;
|
|||
import jdk.internal.logger.LazyLoggers;
|
||||
import jdk.internal.logger.LocalizedLoggerWrapper;
|
||||
import jdk.internal.util.SystemProps;
|
||||
import jdk.internal.vm.Continuation;
|
||||
import jdk.internal.vm.ContinuationScope;
|
||||
import jdk.internal.vm.StackableScope;
|
||||
import jdk.internal.vm.ThreadContainer;
|
||||
import jdk.internal.vm.annotation.IntrinsicCandidate;
|
||||
import jdk.internal.vm.annotation.Stable;
|
||||
import jdk.internal.vm.annotation.ChangesCurrentThread;
|
||||
import sun.nio.fs.DefaultFileSystemProvider;
|
||||
import sun.reflect.annotation.AnnotationType;
|
||||
import sun.nio.ch.Interruptible;
|
||||
|
@ -2054,12 +2061,12 @@ public final class System {
|
|||
/**
|
||||
* Create PrintStream for stdout/err based on encoding.
|
||||
*/
|
||||
private static PrintStream newPrintStream(FileOutputStream fos, String enc) {
|
||||
private static PrintStream newPrintStream(OutputStream out, String enc) {
|
||||
if (enc != null) {
|
||||
return new PrintStream(new BufferedOutputStream(fos, 128), true,
|
||||
return new PrintStream(new BufferedOutputStream(out, 128), true,
|
||||
Charset.forName(enc, UTF_8.INSTANCE));
|
||||
}
|
||||
return new PrintStream(new BufferedOutputStream(fos, 128), true);
|
||||
return new PrintStream(new BufferedOutputStream(out, 128), true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2180,17 +2187,10 @@ public final class System {
|
|||
// classes are used.
|
||||
VM.initializeOSEnvironment();
|
||||
|
||||
// The main thread is not added to its thread group in the same
|
||||
// way as other threads; we must do it ourselves here.
|
||||
Thread current = Thread.currentThread();
|
||||
current.getThreadGroup().add(current);
|
||||
// start Finalizer and Reference Handler threads
|
||||
SharedSecrets.getJavaLangRefAccess().startThreads();
|
||||
|
||||
// Subsystems that are invoked during initialization can invoke
|
||||
// VM.isBooted() in order to avoid doing things that should
|
||||
// wait until the VM is fully initialized. The initialization level
|
||||
// is incremented from 0 to 1 here to indicate the first phase of
|
||||
// initialization has completed.
|
||||
// IMPORTANT: Ensure that this remains the last initialization action!
|
||||
// system properties, java.lang and other core classes are now initialized
|
||||
VM.initLevel(1);
|
||||
}
|
||||
|
||||
|
@ -2513,6 +2513,104 @@ public final class System {
|
|||
public void exit(int statusCode) {
|
||||
Shutdown.exit(statusCode);
|
||||
}
|
||||
|
||||
public Thread[] getAllThreads() {
|
||||
return Thread.getAllThreads();
|
||||
}
|
||||
|
||||
public ThreadContainer threadContainer(Thread thread) {
|
||||
return thread.threadContainer();
|
||||
}
|
||||
|
||||
public void start(Thread thread, ThreadContainer container) {
|
||||
thread.start(container);
|
||||
}
|
||||
|
||||
public StackableScope headStackableScope(Thread thread) {
|
||||
return thread.headStackableScopes();
|
||||
}
|
||||
|
||||
public void setHeadStackableScope(StackableScope scope) {
|
||||
Thread.setHeadStackableScope(scope);
|
||||
}
|
||||
|
||||
public Thread currentCarrierThread() {
|
||||
return Thread.currentCarrierThread();
|
||||
}
|
||||
|
||||
@ChangesCurrentThread
|
||||
public <V> V executeOnCarrierThread(Callable<V> task) throws Exception {
|
||||
Thread thread = Thread.currentThread();
|
||||
if (thread.isVirtual()) {
|
||||
Thread carrier = Thread.currentCarrierThread();
|
||||
carrier.setCurrentThread(carrier);
|
||||
try {
|
||||
return task.call();
|
||||
} finally {
|
||||
carrier.setCurrentThread(thread);
|
||||
}
|
||||
} else {
|
||||
return task.call();
|
||||
}
|
||||
}
|
||||
|
||||
public <T> T getCarrierThreadLocal(ThreadLocal<T> local) {
|
||||
return local.getCarrierThreadLocal();
|
||||
}
|
||||
|
||||
public <T> void setCarrierThreadLocal(ThreadLocal<T> local, T value) {
|
||||
local.setCarrierThreadLocal(value);
|
||||
}
|
||||
|
||||
public Object[] extentLocalCache() {
|
||||
return Thread.extentLocalCache();
|
||||
}
|
||||
|
||||
public void setExtentLocalCache(Object[] cache) {
|
||||
Thread.setExtentLocalCache(cache);
|
||||
}
|
||||
|
||||
public Object extentLocalBindings() {
|
||||
return Thread.extentLocalBindings();
|
||||
}
|
||||
|
||||
public void setExtentLocalBindings(Object bindings) {
|
||||
Thread.setExtentLocalBindings(bindings);
|
||||
}
|
||||
|
||||
public Continuation getContinuation(Thread thread) {
|
||||
return thread.getContinuation();
|
||||
}
|
||||
|
||||
public void setContinuation(Thread thread, Continuation continuation) {
|
||||
thread.setContinuation(continuation);
|
||||
}
|
||||
|
||||
public ContinuationScope virtualThreadContinuationScope() {
|
||||
return VirtualThread.continuationScope();
|
||||
}
|
||||
|
||||
public void parkVirtualThread() {
|
||||
VirtualThread.park();
|
||||
}
|
||||
|
||||
public void parkVirtualThread(long nanos) {
|
||||
VirtualThread.parkNanos(nanos);
|
||||
}
|
||||
|
||||
public void unparkVirtualThread(Thread thread) {
|
||||
if (thread instanceof VirtualThread vthread) {
|
||||
vthread.unpark();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Not a virtual thread");
|
||||
}
|
||||
}
|
||||
|
||||
public StackWalker newStackWalkerInstance(Set<StackWalker.Option> options,
|
||||
ContinuationScope contScope,
|
||||
Continuation continuation) {
|
||||
return StackWalker.newInstance(options, null, contScope, continuation);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
359
src/java.base/share/classes/java/lang/ThreadBuilders.java
Normal file
359
src/java.base/share/classes/java/lang/ThreadBuilders.java
Normal file
|
@ -0,0 +1,359 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package java.lang;
|
||||
|
||||
import java.lang.Thread.Builder;
|
||||
import java.lang.Thread.Builder.OfPlatform;
|
||||
import java.lang.Thread.Builder.OfVirtual;
|
||||
import java.lang.Thread.UncaughtExceptionHandler;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
/**
|
||||
* Defines static methods to create platform and virtual thread builders.
|
||||
*/
|
||||
class ThreadBuilders {
|
||||
|
||||
/**
|
||||
* Base implementation of ThreadBuilder.
|
||||
*/
|
||||
static abstract non-sealed
|
||||
class BaseThreadBuilder<T extends Builder> implements Builder {
|
||||
private String name;
|
||||
private long counter;
|
||||
private int characteristics;
|
||||
private UncaughtExceptionHandler uhe;
|
||||
|
||||
String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
long counter() {
|
||||
return counter;
|
||||
}
|
||||
|
||||
int characteristics() {
|
||||
return characteristics;
|
||||
}
|
||||
|
||||
UncaughtExceptionHandler uncaughtExceptionHandler() {
|
||||
return uhe;
|
||||
}
|
||||
|
||||
String nextThreadName() {
|
||||
if (name != null && counter >= 0) {
|
||||
return name + (counter++);
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public T name(String name) {
|
||||
this.name = Objects.requireNonNull(name);
|
||||
this.counter = -1;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public T name(String prefix, long start) {
|
||||
Objects.requireNonNull(prefix);
|
||||
if (start < 0)
|
||||
throw new IllegalArgumentException("'start' is negative");
|
||||
this.name = prefix;
|
||||
this.counter = start;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public T allowSetThreadLocals(boolean allow) {
|
||||
if (allow) {
|
||||
characteristics &= ~Thread.NO_THREAD_LOCALS;
|
||||
} else {
|
||||
characteristics |= Thread.NO_THREAD_LOCALS;
|
||||
}
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public T inheritInheritableThreadLocals(boolean inherit) {
|
||||
if (inherit) {
|
||||
characteristics &= ~Thread.NO_INHERIT_THREAD_LOCALS;
|
||||
} else {
|
||||
characteristics |= Thread.NO_INHERIT_THREAD_LOCALS;
|
||||
}
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public T uncaughtExceptionHandler(UncaughtExceptionHandler ueh) {
|
||||
this.uhe = Objects.requireNonNull(ueh);
|
||||
return (T) this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ThreadBuilder.OfPlatform implementation.
|
||||
*/
|
||||
static final class PlatformThreadBuilder
|
||||
extends BaseThreadBuilder<OfPlatform> implements OfPlatform {
|
||||
private ThreadGroup group;
|
||||
private boolean daemon;
|
||||
private boolean daemonChanged;
|
||||
private int priority;
|
||||
private long stackSize;
|
||||
|
||||
@Override
|
||||
String nextThreadName() {
|
||||
String name = super.nextThreadName();
|
||||
return (name != null) ? name : Thread.genThreadName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OfPlatform group(ThreadGroup group) {
|
||||
this.group = Objects.requireNonNull(group);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OfPlatform daemon(boolean on) {
|
||||
daemon = on;
|
||||
daemonChanged = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OfPlatform priority(int priority) {
|
||||
if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY)
|
||||
throw new IllegalArgumentException();
|
||||
this.priority = priority;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OfPlatform stackSize(long stackSize) {
|
||||
if (stackSize < 0L)
|
||||
throw new IllegalArgumentException();
|
||||
this.stackSize = stackSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread unstarted(Runnable task) {
|
||||
Objects.requireNonNull(task);
|
||||
String name = nextThreadName();
|
||||
var thread = new Thread(group, name, characteristics(), task, stackSize, null);
|
||||
if (daemonChanged)
|
||||
thread.daemon(daemon);
|
||||
if (priority != 0)
|
||||
thread.priority(priority);
|
||||
UncaughtExceptionHandler uhe = uncaughtExceptionHandler();
|
||||
if (uhe != null)
|
||||
thread.uncaughtExceptionHandler(uhe);
|
||||
return thread;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread start(Runnable task) {
|
||||
Thread thread = unstarted(task);
|
||||
thread.start();
|
||||
return thread;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThreadFactory factory() {
|
||||
return new PlatformThreadFactory(group, name(), counter(), characteristics(),
|
||||
daemonChanged, daemon, priority, stackSize, uncaughtExceptionHandler());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* ThreadBuilder.OfVirtual implementation.
|
||||
*/
|
||||
static final class VirtualThreadBuilder
|
||||
extends BaseThreadBuilder<OfVirtual> implements OfVirtual {
|
||||
private Executor scheduler; // set by tests
|
||||
|
||||
@Override
|
||||
public Thread unstarted(Runnable task) {
|
||||
Objects.requireNonNull(task);
|
||||
var thread = new VirtualThread(scheduler, nextThreadName(), characteristics(), task);
|
||||
UncaughtExceptionHandler uhe = uncaughtExceptionHandler();
|
||||
if (uhe != null)
|
||||
thread.uncaughtExceptionHandler(uhe);
|
||||
return thread;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread start(Runnable task) {
|
||||
Thread thread = unstarted(task);
|
||||
thread.start();
|
||||
return thread;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThreadFactory factory() {
|
||||
return new VirtualThreadFactory(scheduler, name(), counter(), characteristics(),
|
||||
uncaughtExceptionHandler());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base ThreadFactory implementation.
|
||||
*/
|
||||
private static abstract class BaseThreadFactory implements ThreadFactory {
|
||||
private static final VarHandle COUNT;
|
||||
static {
|
||||
try {
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
COUNT = l.findVarHandle(BaseThreadFactory.class, "count", long.class);
|
||||
} catch (Exception e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
private final String name;
|
||||
private final int characteristics;
|
||||
private final UncaughtExceptionHandler uhe;
|
||||
|
||||
private final boolean hasCounter;
|
||||
private volatile long count;
|
||||
|
||||
BaseThreadFactory(String name,
|
||||
long start,
|
||||
int characteristics,
|
||||
UncaughtExceptionHandler uhe) {
|
||||
this.name = name;
|
||||
if (name != null && start >= 0) {
|
||||
this.hasCounter = true;
|
||||
this.count = start;
|
||||
} else {
|
||||
this.hasCounter = false;
|
||||
}
|
||||
this.characteristics = characteristics;
|
||||
this.uhe = uhe;
|
||||
}
|
||||
|
||||
int characteristics() {
|
||||
return characteristics;
|
||||
}
|
||||
|
||||
UncaughtExceptionHandler uncaughtExceptionHandler() {
|
||||
return uhe;
|
||||
}
|
||||
|
||||
String nextThreadName() {
|
||||
if (hasCounter) {
|
||||
return name + (long) COUNT.getAndAdd(this, 1);
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ThreadFactory for platform threads.
|
||||
*/
|
||||
private static class PlatformThreadFactory extends BaseThreadFactory {
|
||||
private final ThreadGroup group;
|
||||
private final boolean daemonChanged;
|
||||
private final boolean daemon;
|
||||
private final int priority;
|
||||
private final long stackSize;
|
||||
|
||||
PlatformThreadFactory(ThreadGroup group,
|
||||
String name,
|
||||
long start,
|
||||
int characteristics,
|
||||
boolean daemonChanged,
|
||||
boolean daemon,
|
||||
int priority,
|
||||
long stackSize,
|
||||
UncaughtExceptionHandler uhe) {
|
||||
super(name, start, characteristics, uhe);
|
||||
this.group = group;
|
||||
this.daemonChanged = daemonChanged;
|
||||
this.daemon = daemon;
|
||||
this.priority = priority;
|
||||
this.stackSize = stackSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
String nextThreadName() {
|
||||
String name = super.nextThreadName();
|
||||
return (name != null) ? name : Thread.genThreadName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable task) {
|
||||
Objects.requireNonNull(task);
|
||||
String name = nextThreadName();
|
||||
Thread thread = new Thread(group, name, characteristics(), task, stackSize, null);
|
||||
if (daemonChanged)
|
||||
thread.daemon(daemon);
|
||||
if (priority != 0)
|
||||
thread.priority(priority);
|
||||
UncaughtExceptionHandler uhe = uncaughtExceptionHandler();
|
||||
if (uhe != null)
|
||||
thread.uncaughtExceptionHandler(uhe);
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ThreadFactory for virtual threads.
|
||||
*/
|
||||
private static class VirtualThreadFactory extends BaseThreadFactory {
|
||||
private final Executor scheduler;
|
||||
|
||||
VirtualThreadFactory(Executor scheduler,
|
||||
String name,
|
||||
long start,
|
||||
int characteristics,
|
||||
UncaughtExceptionHandler uhe) {
|
||||
super(name, start, characteristics, uhe);
|
||||
this.scheduler = scheduler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable task) {
|
||||
Objects.requireNonNull(task);
|
||||
String name = nextThreadName();
|
||||
Thread thread = new VirtualThread(scheduler, name, characteristics(), task);
|
||||
UncaughtExceptionHandler uhe = uncaughtExceptionHandler();
|
||||
if (uhe != null)
|
||||
thread.uncaughtExceptionHandler(uhe);
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -24,12 +24,12 @@
|
|||
*/
|
||||
|
||||
package java.lang;
|
||||
import jdk.internal.misc.TerminatingThreadLocal;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Supplier;
|
||||
import jdk.internal.misc.TerminatingThreadLocal;
|
||||
|
||||
/**
|
||||
* This class provides thread-local variables. These variables differ from
|
||||
|
@ -156,21 +156,40 @@ public class ThreadLocal<T> {
|
|||
* thread-local variable. If the variable has no value for the
|
||||
* current thread, it is first initialized to the value returned
|
||||
* by an invocation of the {@link #initialValue} method.
|
||||
* If the current thread does not support thread locals then
|
||||
* this method returns its {@link #initialValue} (or {@code null}
|
||||
* if the {@code initialValue} method is not overridden).
|
||||
*
|
||||
* @return the current thread's value of this thread-local
|
||||
* @see Thread.Builder#allowSetThreadLocals(boolean)
|
||||
*/
|
||||
public T get() {
|
||||
Thread t = Thread.currentThread();
|
||||
return get(Thread.currentThread());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value in the current carrier thread's copy of this
|
||||
* thread-local variable.
|
||||
*/
|
||||
T getCarrierThreadLocal() {
|
||||
return get(Thread.currentCarrierThread());
|
||||
}
|
||||
|
||||
private T get(Thread t) {
|
||||
ThreadLocalMap map = getMap(t);
|
||||
if (map != null) {
|
||||
ThreadLocalMap.Entry e = map.getEntry(this);
|
||||
if (e != null) {
|
||||
@SuppressWarnings("unchecked")
|
||||
T result = (T)e.value;
|
||||
return result;
|
||||
if (map == ThreadLocalMap.NOT_SUPPORTED) {
|
||||
return initialValue();
|
||||
} else {
|
||||
ThreadLocalMap.Entry e = map.getEntry(this);
|
||||
if (e != null) {
|
||||
@SuppressWarnings("unchecked")
|
||||
T result = (T) e.value;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return setInitialValue();
|
||||
return setInitialValue(t);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -183,7 +202,11 @@ public class ThreadLocal<T> {
|
|||
boolean isPresent() {
|
||||
Thread t = Thread.currentThread();
|
||||
ThreadLocalMap map = getMap(t);
|
||||
return map != null && map.getEntry(this) != null;
|
||||
if (map != null && map != ThreadLocalMap.NOT_SUPPORTED) {
|
||||
return map.getEntry(this) != null;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -192,10 +215,10 @@ public class ThreadLocal<T> {
|
|||
*
|
||||
* @return the initial value
|
||||
*/
|
||||
private T setInitialValue() {
|
||||
private T setInitialValue(Thread t) {
|
||||
T value = initialValue();
|
||||
Thread t = Thread.currentThread();
|
||||
ThreadLocalMap map = getMap(t);
|
||||
assert map != ThreadLocalMap.NOT_SUPPORTED;
|
||||
if (map != null) {
|
||||
map.set(this, value);
|
||||
} else {
|
||||
|
@ -215,10 +238,25 @@ public class ThreadLocal<T> {
|
|||
*
|
||||
* @param value the value to be stored in the current thread's copy of
|
||||
* this thread-local.
|
||||
*
|
||||
* @throws UnsupportedOperationException if the current thread is not
|
||||
* allowed to set its copy of thread-local variables
|
||||
*
|
||||
* @see Thread.Builder#allowSetThreadLocals(boolean)
|
||||
*/
|
||||
public void set(T value) {
|
||||
Thread t = Thread.currentThread();
|
||||
set(Thread.currentThread(), value);
|
||||
}
|
||||
|
||||
void setCarrierThreadLocal(T value) {
|
||||
set(Thread.currentCarrierThread(), value);
|
||||
}
|
||||
|
||||
private void set(Thread t, T value) {
|
||||
ThreadLocalMap map = getMap(t);
|
||||
if (map == ThreadLocalMap.NOT_SUPPORTED) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
if (map != null) {
|
||||
map.set(this, value);
|
||||
} else {
|
||||
|
@ -239,7 +277,7 @@ public class ThreadLocal<T> {
|
|||
*/
|
||||
public void remove() {
|
||||
ThreadLocalMap m = getMap(Thread.currentThread());
|
||||
if (m != null) {
|
||||
if (m != null && m != ThreadLocalMap.NOT_SUPPORTED) {
|
||||
m.remove(this);
|
||||
}
|
||||
}
|
||||
|
@ -337,6 +375,9 @@ public class ThreadLocal<T> {
|
|||
}
|
||||
}
|
||||
|
||||
// Placeholder when thread locals not supported
|
||||
static final ThreadLocalMap NOT_SUPPORTED = new ThreadLocalMap();
|
||||
|
||||
/**
|
||||
* The initial capacity -- MUST be a power of two.
|
||||
*/
|
||||
|
@ -379,6 +420,12 @@ public class ThreadLocal<T> {
|
|||
return ((i - 1 >= 0) ? i - 1 : len - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new map without a table.
|
||||
*/
|
||||
private ThreadLocalMap() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new map initially containing (firstKey, firstValue).
|
||||
* ThreadLocalMaps are constructed lazily, so we only create
|
||||
|
@ -421,6 +468,13 @@ public class ThreadLocal<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of elements in the map.
|
||||
*/
|
||||
int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entry associated with key. This method
|
||||
* itself handles only the fast path: a direct hit of existing
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1994, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1994, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -27,6 +27,8 @@ package java.lang;
|
|||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.InternalLock;
|
||||
|
||||
/**
|
||||
* The {@code Throwable} class is the superclass of all errors and
|
||||
|
@ -659,27 +661,39 @@ public class Throwable implements Serializable {
|
|||
}
|
||||
|
||||
private void printStackTrace(PrintStreamOrWriter s) {
|
||||
Object lock = s.lock();
|
||||
if (lock instanceof InternalLock locker) {
|
||||
locker.lock();
|
||||
try {
|
||||
lockedPrintStackTrace(s);
|
||||
} finally {
|
||||
locker.unlock();
|
||||
}
|
||||
} else synchronized (lock) {
|
||||
lockedPrintStackTrace(s);
|
||||
}
|
||||
}
|
||||
|
||||
private void lockedPrintStackTrace(PrintStreamOrWriter s) {
|
||||
// Guard against malicious overrides of Throwable.equals by
|
||||
// using a Set with identity equality semantics.
|
||||
Set<Throwable> dejaVu = Collections.newSetFromMap(new IdentityHashMap<>());
|
||||
dejaVu.add(this);
|
||||
|
||||
synchronized (s.lock()) {
|
||||
// Print our stack trace
|
||||
s.println(this);
|
||||
StackTraceElement[] trace = getOurStackTrace();
|
||||
for (StackTraceElement traceElement : trace)
|
||||
s.println("\tat " + traceElement);
|
||||
// Print our stack trace
|
||||
s.println(this);
|
||||
StackTraceElement[] trace = getOurStackTrace();
|
||||
for (StackTraceElement traceElement : trace)
|
||||
s.println("\tat " + traceElement);
|
||||
|
||||
// Print suppressed exceptions, if any
|
||||
for (Throwable se : getSuppressed())
|
||||
se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION, "\t", dejaVu);
|
||||
// Print suppressed exceptions, if any
|
||||
for (Throwable se : getSuppressed())
|
||||
se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION, "\t", dejaVu);
|
||||
|
||||
// Print cause, if any
|
||||
Throwable ourCause = getCause();
|
||||
if (ourCause != null)
|
||||
ourCause.printEnclosedStackTrace(s, trace, CAUSE_CAPTION, "", dejaVu);
|
||||
}
|
||||
// Print cause, if any
|
||||
Throwable ourCause = getCause();
|
||||
if (ourCause != null)
|
||||
ourCause.printEnclosedStackTrace(s, trace, CAUSE_CAPTION, "", dejaVu);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -691,7 +705,7 @@ public class Throwable implements Serializable {
|
|||
String caption,
|
||||
String prefix,
|
||||
Set<Throwable> dejaVu) {
|
||||
assert Thread.holdsLock(s.lock());
|
||||
assert s.isLockedByCurrentThread();
|
||||
if (dejaVu.contains(this)) {
|
||||
s.println(prefix + caption + "[CIRCULAR REFERENCE: " + this + "]");
|
||||
} else {
|
||||
|
@ -743,6 +757,15 @@ public class Throwable implements Serializable {
|
|||
/** Returns the object to be locked when using this StreamOrWriter */
|
||||
abstract Object lock();
|
||||
|
||||
boolean isLockedByCurrentThread() {
|
||||
Object lock = lock();
|
||||
if (lock instanceof InternalLock locker) {
|
||||
return locker.isHeldByCurrentThread();
|
||||
} else {
|
||||
return Thread.holdsLock(lock);
|
||||
}
|
||||
}
|
||||
|
||||
/** Prints the specified string as a line on this StreamOrWriter */
|
||||
abstract void println(Object o);
|
||||
}
|
||||
|
@ -755,7 +778,7 @@ public class Throwable implements Serializable {
|
|||
}
|
||||
|
||||
Object lock() {
|
||||
return printStream;
|
||||
return SharedSecrets.getJavaIOPrintStreamAccess().lock(printStream);
|
||||
}
|
||||
|
||||
void println(Object o) {
|
||||
|
@ -771,7 +794,7 @@ public class Throwable implements Serializable {
|
|||
}
|
||||
|
||||
Object lock() {
|
||||
return printWriter;
|
||||
return SharedSecrets.getJavaIOPrintWriterAccess().lock(printWriter);
|
||||
}
|
||||
|
||||
void println(Object o) {
|
||||
|
@ -833,11 +856,13 @@ public class Throwable implements Serializable {
|
|||
private synchronized StackTraceElement[] getOurStackTrace() {
|
||||
// Initialize stack trace field with information from
|
||||
// backtrace if this is the first call to this method
|
||||
if (stackTrace == UNASSIGNED_STACK ||
|
||||
(stackTrace == null && backtrace != null) /* Out of protocol state */) {
|
||||
stackTrace = StackTraceElement.of(this, depth);
|
||||
} else if (stackTrace == null) {
|
||||
return UNASSIGNED_STACK;
|
||||
if (stackTrace == UNASSIGNED_STACK || stackTrace == null) {
|
||||
if (backtrace != null) { /* Out of protocol state */
|
||||
stackTrace = StackTraceElement.of(backtrace, depth);
|
||||
} else {
|
||||
// no backtrace, fillInStackTrace overridden or not called
|
||||
return UNASSIGNED_STACK;
|
||||
}
|
||||
}
|
||||
return stackTrace;
|
||||
}
|
||||
|
|
1105
src/java.base/share/classes/java/lang/VirtualThread.java
Normal file
1105
src/java.base/share/classes/java/lang/VirtualThread.java
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package java.lang;
|
||||
|
||||
/**
|
||||
* Thrown to indicate that a method has been called on the wrong thread.
|
||||
*
|
||||
* @since 19
|
||||
*/
|
||||
public final class WrongThreadException extends RuntimeException {
|
||||
@java.io.Serial
|
||||
static final long serialVersionUID = 4676498871006316905L;
|
||||
|
||||
/**
|
||||
* Constructs a WrongThreadException with no detail message.
|
||||
*/
|
||||
public WrongThreadException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a WrongThreadException with the given detail message.
|
||||
*
|
||||
* @param s the String that contains a detailed message, can be null
|
||||
*/
|
||||
public WrongThreadException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a WrongThreadException with the given detail message and cause.
|
||||
*
|
||||
* @param message the detail message, can be null
|
||||
* @param cause the cause, can be null
|
||||
*/
|
||||
public WrongThreadException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a WrongThreadException with the given cause and a detail
|
||||
* message of {@code (cause==null ? null : cause.toString())} (which
|
||||
* typically contains the class and detail message of {@code cause}).
|
||||
*
|
||||
* @param cause the cause, can be null
|
||||
*/
|
||||
public WrongThreadException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
|
@ -42,6 +42,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.vm.annotation.Stable;
|
||||
import sun.invoke.util.BytecodeDescriptor;
|
||||
import sun.invoke.util.VerifyType;
|
||||
|
@ -1402,7 +1403,7 @@ s.writeObject(this.parameterArray());
|
|||
|
||||
public ConcurrentWeakInternSet() {
|
||||
this.map = new ConcurrentHashMap<>(512);
|
||||
this.stale = new ReferenceQueue<>();
|
||||
this.stale = SharedSecrets.getJavaLangRefAccess().newNativeReferenceQueue();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -35,7 +35,7 @@ final class Finalizer extends FinalReference<Object> { /* Package-private; must
|
|||
same package as the Reference
|
||||
class */
|
||||
|
||||
private static ReferenceQueue<Object> queue = new ReferenceQueue<>();
|
||||
private static ReferenceQueue<Object> queue = new NativeReferenceQueue<>();
|
||||
|
||||
/** Head of doubly linked list of Finalizers awaiting finalization. */
|
||||
private static Finalizer unfinalized = null;
|
||||
|
@ -166,16 +166,6 @@ final class Finalizer extends FinalReference<Object> { /* Package-private; must
|
|||
if (running)
|
||||
return;
|
||||
|
||||
// Finalizer thread starts before System.initializeSystemClass
|
||||
// is called. Wait until JavaLangAccess is available
|
||||
while (VM.initLevel() == 0) {
|
||||
// delay until VM completes initialization
|
||||
try {
|
||||
VM.awaitInitLevel(1);
|
||||
} catch (InterruptedException x) {
|
||||
// ignore and continue
|
||||
}
|
||||
}
|
||||
final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
|
||||
running = true;
|
||||
for (;;) {
|
||||
|
@ -189,17 +179,15 @@ final class Finalizer extends FinalReference<Object> { /* Package-private; must
|
|||
}
|
||||
}
|
||||
|
||||
static {
|
||||
/**
|
||||
* Start the Finalizer thread as a daemon thread.
|
||||
*/
|
||||
static void startFinalizerThread(ThreadGroup tg) {
|
||||
if (ENABLED) {
|
||||
ThreadGroup tg = Thread.currentThread().getThreadGroup();
|
||||
for (ThreadGroup tgn = tg;
|
||||
tgn != null;
|
||||
tg = tgn, tgn = tg.getParent());
|
||||
Thread finalizer = new FinalizerThread(tg);
|
||||
finalizer.setPriority(Thread.MAX_PRIORITY - 2);
|
||||
finalizer.setDaemon(true);
|
||||
finalizer.start();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package java.lang.ref;
|
||||
|
||||
/**
|
||||
* An implementation of a ReferenceQueue that uses native monitors.
|
||||
* The use of java.util.concurrent.lock locks interacts with various mechanisms,
|
||||
* such as virtual threads and ForkJoinPool, that might not be appropriate for some
|
||||
* low-level mechanisms, in particular MethodType's weak intern set.
|
||||
*/
|
||||
final class NativeReferenceQueue<T> extends ReferenceQueue<T> {
|
||||
public NativeReferenceQueue() {
|
||||
super(0);
|
||||
}
|
||||
|
||||
private static class Lock { };
|
||||
private final Lock lock = new Lock();
|
||||
|
||||
@Override
|
||||
void signal() {
|
||||
lock.notifyAll();
|
||||
}
|
||||
@Override
|
||||
void await() throws InterruptedException {
|
||||
lock.wait();
|
||||
}
|
||||
|
||||
@Override
|
||||
void await(long timeoutMillis) throws InterruptedException {
|
||||
lock.wait(timeoutMillis);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean enqueue(Reference<? extends T> r) {
|
||||
synchronized(lock) {
|
||||
return enqueue0(r);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Reference<? extends T> poll() {
|
||||
if (headIsNull())
|
||||
return null;
|
||||
|
||||
synchronized(lock) {
|
||||
return poll0();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Reference<? extends T> remove(long timeout)
|
||||
throws IllegalArgumentException, InterruptedException {
|
||||
if (timeout < 0)
|
||||
throw new IllegalArgumentException("Negative timeout value");
|
||||
if (timeout == 0)
|
||||
return remove();
|
||||
|
||||
synchronized(lock) {
|
||||
return remove0(timeout);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Reference<? extends T> remove() throws InterruptedException {
|
||||
synchronized(lock) {
|
||||
return remove0();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
package java.lang.ref;
|
||||
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
import jdk.internal.vm.annotation.IntrinsicCandidate;
|
||||
import jdk.internal.access.JavaLangRefAccess;
|
||||
|
@ -192,27 +193,16 @@ public abstract sealed class Reference<T>
|
|||
/* High-priority thread to enqueue pending References
|
||||
*/
|
||||
private static class ReferenceHandler extends Thread {
|
||||
|
||||
private static void ensureClassInitialized(Class<?> clazz) {
|
||||
try {
|
||||
Class.forName(clazz.getName(), true, clazz.getClassLoader());
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw (Error) new NoClassDefFoundError(e.getMessage()).initCause(e);
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
// pre-load and initialize Cleaner class so that we don't
|
||||
// get into trouble later in the run loop if there's
|
||||
// memory shortage while loading/initializing it lazily.
|
||||
ensureClassInitialized(Cleaner.class);
|
||||
}
|
||||
|
||||
ReferenceHandler(ThreadGroup g, String name) {
|
||||
super(g, null, name, 0, false);
|
||||
}
|
||||
|
||||
public void run() {
|
||||
// pre-load and initialize Cleaner class so that we don't
|
||||
// get into trouble later in the run loop if there's
|
||||
// memory shortage while loading/initializing it lazily.
|
||||
Unsafe.getUnsafe().ensureClassInitialized(Cleaner.class);
|
||||
|
||||
while (true) {
|
||||
processPendingReferences();
|
||||
}
|
||||
|
@ -302,11 +292,10 @@ public abstract sealed class Reference<T>
|
|||
}
|
||||
}
|
||||
|
||||
static {
|
||||
ThreadGroup tg = Thread.currentThread().getThreadGroup();
|
||||
for (ThreadGroup tgn = tg;
|
||||
tgn != null;
|
||||
tg = tgn, tgn = tg.getParent());
|
||||
/**
|
||||
* Start the Reference Handler thread as a daemon thread.
|
||||
*/
|
||||
static void startReferenceHandlerThread(ThreadGroup tg) {
|
||||
Thread handler = new ReferenceHandler(tg, "Reference Handler");
|
||||
/* If there were a special system-only priority greater than
|
||||
* MAX_PRIORITY, it would be used here
|
||||
|
@ -314,9 +303,21 @@ public abstract sealed class Reference<T>
|
|||
handler.setPriority(Thread.MAX_PRIORITY);
|
||||
handler.setDaemon(true);
|
||||
handler.start();
|
||||
}
|
||||
|
||||
static {
|
||||
// provide access in SharedSecrets
|
||||
SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {
|
||||
@Override
|
||||
public void startThreads() {
|
||||
ThreadGroup tg = Thread.currentThread().getThreadGroup();
|
||||
for (ThreadGroup tgn = tg;
|
||||
tgn != null;
|
||||
tg = tgn, tgn = tg.getParent());
|
||||
Reference.startReferenceHandlerThread(tg);
|
||||
Finalizer.startFinalizerThread(tg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean waitForReferenceProcessing()
|
||||
throws InterruptedException
|
||||
|
@ -328,6 +329,11 @@ public abstract sealed class Reference<T>
|
|||
public void runFinalization() {
|
||||
Finalizer.runFinalization();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ReferenceQueue<T> newNativeReferenceQueue() {
|
||||
return new NativeReferenceQueue<T>();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,9 @@
|
|||
|
||||
package java.lang.ref;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Consumer;
|
||||
import jdk.internal.misc.VM;
|
||||
|
||||
|
@ -38,13 +41,10 @@ import jdk.internal.misc.VM;
|
|||
*/
|
||||
|
||||
public class ReferenceQueue<T> {
|
||||
|
||||
/**
|
||||
* Constructs a new reference-object queue.
|
||||
*/
|
||||
public ReferenceQueue() { }
|
||||
|
||||
private static class Null extends ReferenceQueue<Object> {
|
||||
public Null() { super(0); }
|
||||
|
||||
@Override
|
||||
boolean enqueue(Reference<?> r) {
|
||||
return false;
|
||||
}
|
||||
|
@ -53,37 +53,65 @@ public class ReferenceQueue<T> {
|
|||
static final ReferenceQueue<Object> NULL = new Null();
|
||||
static final ReferenceQueue<Object> ENQUEUED = new Null();
|
||||
|
||||
private static class Lock { };
|
||||
private final Lock lock = new Lock();
|
||||
private volatile Reference<? extends T> head;
|
||||
private long queueLength = 0;
|
||||
|
||||
boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */
|
||||
synchronized (lock) {
|
||||
// Check that since getting the lock this reference hasn't already been
|
||||
// enqueued (and even then removed)
|
||||
ReferenceQueue<?> queue = r.queue;
|
||||
if ((queue == NULL) || (queue == ENQUEUED)) {
|
||||
return false;
|
||||
}
|
||||
assert queue == this;
|
||||
// Self-loop end, so if a FinalReference it remains inactive.
|
||||
r.next = (head == null) ? r : head;
|
||||
head = r;
|
||||
queueLength++;
|
||||
// Update r.queue *after* adding to list, to avoid race
|
||||
// with concurrent enqueued checks and fast-path poll().
|
||||
// Volatiles ensure ordering.
|
||||
r.queue = ENQUEUED;
|
||||
if (r instanceof FinalReference) {
|
||||
VM.addFinalRefCount(1);
|
||||
}
|
||||
lock.notifyAll();
|
||||
return true;
|
||||
}
|
||||
private final ReentrantLock lock;
|
||||
private final Condition notEmpty;
|
||||
|
||||
void signal() {
|
||||
notEmpty.signalAll();
|
||||
}
|
||||
|
||||
private Reference<? extends T> reallyPoll() { /* Must hold lock */
|
||||
void await() throws InterruptedException {
|
||||
notEmpty.await();
|
||||
}
|
||||
|
||||
void await(long timeoutMillis) throws InterruptedException {
|
||||
notEmpty.await(timeoutMillis, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new reference-object queue.
|
||||
*/
|
||||
public ReferenceQueue() {
|
||||
this.lock = new ReentrantLock();
|
||||
this.notEmpty = lock.newCondition();
|
||||
}
|
||||
|
||||
ReferenceQueue(int dummy) {
|
||||
this.lock = null;
|
||||
this.notEmpty = null;
|
||||
}
|
||||
|
||||
final boolean enqueue0(Reference<? extends T> r) { // must hold lock
|
||||
// Check that since getting the lock this reference hasn't already been
|
||||
// enqueued (and even then removed)
|
||||
ReferenceQueue<?> queue = r.queue;
|
||||
if ((queue == NULL) || (queue == ENQUEUED)) {
|
||||
return false;
|
||||
}
|
||||
assert queue == this;
|
||||
// Self-loop end, so if a FinalReference it remains inactive.
|
||||
r.next = (head == null) ? r : head;
|
||||
head = r;
|
||||
queueLength++;
|
||||
// Update r.queue *after* adding to list, to avoid race
|
||||
// with concurrent enqueued checks and fast-path poll().
|
||||
// Volatiles ensure ordering.
|
||||
r.queue = ENQUEUED;
|
||||
if (r instanceof FinalReference) {
|
||||
VM.addFinalRefCount(1);
|
||||
}
|
||||
signal();
|
||||
return true;
|
||||
}
|
||||
|
||||
final boolean headIsNull() {
|
||||
return head == null;
|
||||
}
|
||||
|
||||
final Reference<? extends T> poll0() { // must hold lock
|
||||
Reference<? extends T> r = head;
|
||||
if (r != null) {
|
||||
r.queue = NULL;
|
||||
|
@ -106,6 +134,40 @@ public class ReferenceQueue<T> {
|
|||
return null;
|
||||
}
|
||||
|
||||
final Reference<? extends T> remove0(long timeout)
|
||||
throws IllegalArgumentException, InterruptedException { // must hold lock
|
||||
Reference<? extends T> r = poll0();
|
||||
if (r != null) return r;
|
||||
long start = System.nanoTime();
|
||||
for (;;) {
|
||||
await(timeout);
|
||||
r = poll0();
|
||||
if (r != null) return r;
|
||||
|
||||
long end = System.nanoTime();
|
||||
timeout -= (end - start) / 1000_000;
|
||||
if (timeout <= 0) return null;
|
||||
start = end;
|
||||
}
|
||||
}
|
||||
|
||||
final Reference<? extends T> remove0() throws InterruptedException { // must hold lock
|
||||
for (;;) {
|
||||
var r = poll0();
|
||||
if (r != null) return r;
|
||||
await();
|
||||
}
|
||||
}
|
||||
|
||||
boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */
|
||||
lock.lock();
|
||||
try {
|
||||
return enqueue0(r);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls this queue to see if a reference object is available. If one is
|
||||
* available without further delay then it is removed from the queue and
|
||||
|
@ -115,10 +177,13 @@ public class ReferenceQueue<T> {
|
|||
* otherwise {@code null}
|
||||
*/
|
||||
public Reference<? extends T> poll() {
|
||||
if (head == null)
|
||||
if (headIsNull())
|
||||
return null;
|
||||
synchronized (lock) {
|
||||
return reallyPoll();
|
||||
lock.lock();
|
||||
try {
|
||||
return poll0();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,27 +207,17 @@ public class ReferenceQueue<T> {
|
|||
* @throws InterruptedException
|
||||
* If the timeout wait is interrupted
|
||||
*/
|
||||
public Reference<? extends T> remove(long timeout)
|
||||
throws IllegalArgumentException, InterruptedException
|
||||
{
|
||||
if (timeout < 0) {
|
||||
public Reference<? extends T> remove(long timeout) throws InterruptedException {
|
||||
if (timeout < 0)
|
||||
throw new IllegalArgumentException("Negative timeout value");
|
||||
}
|
||||
synchronized (lock) {
|
||||
Reference<? extends T> r = reallyPoll();
|
||||
if (r != null) return r;
|
||||
long start = (timeout == 0) ? 0 : System.nanoTime();
|
||||
for (;;) {
|
||||
lock.wait(timeout);
|
||||
r = reallyPoll();
|
||||
if (r != null) return r;
|
||||
if (timeout != 0) {
|
||||
long end = System.nanoTime();
|
||||
timeout -= (end - start) / 1000_000;
|
||||
if (timeout <= 0) return null;
|
||||
start = end;
|
||||
}
|
||||
}
|
||||
if (timeout == 0)
|
||||
return remove();
|
||||
|
||||
lock.lock();
|
||||
try {
|
||||
return remove0(timeout);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,7 +229,12 @@ public class ReferenceQueue<T> {
|
|||
* @throws InterruptedException If the wait is interrupted
|
||||
*/
|
||||
public Reference<? extends T> remove() throws InterruptedException {
|
||||
return remove(0);
|
||||
lock.lock();
|
||||
try {
|
||||
return remove0();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -29,11 +29,8 @@ import java.io.IOException;
|
|||
import java.io.UncheckedIOException;
|
||||
import java.nio.channels.DatagramChannel;
|
||||
import java.nio.channels.MulticastChannel;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import sun.net.NetProperties;
|
||||
import sun.nio.ch.DefaultSelectorProvider;
|
||||
|
||||
/**
|
||||
|
@ -666,17 +663,33 @@ public class DatagramSocket implements java.io.Closeable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Receives a datagram packet from this socket. When this method
|
||||
* returns, the {@code DatagramPacket}'s buffer is filled with
|
||||
* the data received. The datagram packet also contains the sender's
|
||||
* Receives a datagram packet from this socket. This method blocks until a
|
||||
* datagram is received.
|
||||
*
|
||||
* When this method returns, the {@code DatagramPacket}'s buffer is filled
|
||||
* with the data received. The datagram packet also contains the sender's
|
||||
* IP address, and the port number on the sender's machine.
|
||||
* <p>
|
||||
* This method blocks until a datagram is received. The
|
||||
* {@code length} field of the datagram packet object contains
|
||||
* The {@code length} field of the datagram packet object contains
|
||||
* the length of the received message. If the message is longer than
|
||||
* the packet's length, the message is truncated.
|
||||
* <p>
|
||||
* If there is a security manager, and the socket is not currently
|
||||
*
|
||||
* <p> This method is {@linkplain Thread#interrupt() interruptible} in the
|
||||
* following circumstances:
|
||||
* <ol>
|
||||
* <li> The datagram socket is {@linkplain DatagramChannel#socket() associated}
|
||||
* with a {@link DatagramChannel DatagramChannel}. In that case,
|
||||
* interrupting a thread receiving a datagram packet will close the
|
||||
* underlying channel and cause this method to throw {@link
|
||||
* java.nio.channels.ClosedByInterruptException} with the interrupt
|
||||
* status set.
|
||||
* <li> The datagram socket uses the system-default socket implementation and
|
||||
* a {@linkplain Thread#isVirtual() virtual thread} is receiving a
|
||||
* datagram packet. In that case, interrupting the virtual thread will
|
||||
* cause it to wakeup and close the socket. This method will then throw
|
||||
* {@code SocketException} with the interrupt status set.
|
||||
* </ol>
|
||||
*
|
||||
* <p> If there is a security manager, and the socket is not currently
|
||||
* connected to a remote address, a packet cannot be received if the
|
||||
* security manager's {@code checkAccept} method does not allow it.
|
||||
* Datagrams that are not permitted by the security manager are silently
|
||||
|
|
|
@ -55,10 +55,10 @@ import java.util.Arrays;
|
|||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.internal.misc.VM;
|
||||
|
||||
import jdk.internal.access.JavaNetInetAddressAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.Blocker;
|
||||
import jdk.internal.misc.VM;
|
||||
import jdk.internal.vm.annotation.Stable;
|
||||
import sun.net.ResolverProviderConfiguration;
|
||||
import sun.security.action.*;
|
||||
|
@ -972,6 +972,7 @@ public sealed class InetAddress implements Serializable permits Inet4Address, In
|
|||
// in cache when the result is obtained
|
||||
private static final class NameServiceAddresses implements Addresses {
|
||||
private final String host;
|
||||
private final ReentrantLock lookupLock = new ReentrantLock();
|
||||
|
||||
NameServiceAddresses(String host) {
|
||||
this.host = host;
|
||||
|
@ -982,7 +983,8 @@ public sealed class InetAddress implements Serializable permits Inet4Address, In
|
|||
Addresses addresses;
|
||||
// only one thread is doing lookup to name service
|
||||
// for particular host at any time.
|
||||
synchronized (this) {
|
||||
lookupLock.lock();
|
||||
try {
|
||||
// re-check that we are still us + re-install us if slot empty
|
||||
addresses = cache.putIfAbsent(host, this);
|
||||
if (addresses == null) {
|
||||
|
@ -1030,6 +1032,8 @@ public sealed class InetAddress implements Serializable permits Inet4Address, In
|
|||
return inetAddresses;
|
||||
}
|
||||
// else addresses != this
|
||||
} finally {
|
||||
lookupLock.unlock();
|
||||
}
|
||||
// delegate to different addresses when we are already replaced
|
||||
// but outside of synchronized block to avoid any chance of dead-locking
|
||||
|
@ -1049,16 +1053,27 @@ public sealed class InetAddress implements Serializable permits Inet4Address, In
|
|||
throws UnknownHostException {
|
||||
Objects.requireNonNull(host);
|
||||
Objects.requireNonNull(policy);
|
||||
return Arrays.stream(impl.lookupAllHostAddr(host, policy));
|
||||
InetAddress[] addrs;
|
||||
long comp = Blocker.begin();
|
||||
try {
|
||||
addrs = impl.lookupAllHostAddr(host, policy);
|
||||
} finally {
|
||||
Blocker.end(comp);
|
||||
}
|
||||
return Arrays.stream(addrs);
|
||||
}
|
||||
|
||||
public String lookupByAddress(byte[] addr)
|
||||
throws UnknownHostException {
|
||||
public String lookupByAddress(byte[] addr) throws UnknownHostException {
|
||||
Objects.requireNonNull(addr);
|
||||
if (addr.length != Inet4Address.INADDRSZ && addr.length != Inet6Address.INADDRSZ) {
|
||||
throw new IllegalArgumentException("Invalid address length");
|
||||
}
|
||||
return impl.getHostByAddr(addr);
|
||||
long comp = Blocker.begin();
|
||||
try {
|
||||
return impl.getHostByAddr(addr);
|
||||
} finally {
|
||||
Blocker.end(comp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1995, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1995, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -121,11 +121,10 @@ public class MulticastSocket extends DatagramSocket {
|
|||
* Create a MulticastSocket that delegates to the given delegate if not null.
|
||||
* @param delegate the delegate, can be null.
|
||||
*/
|
||||
MulticastSocket(MulticastSocket delegate) {
|
||||
MulticastSocket(MulticastSocket delegate) {
|
||||
super(delegate);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a multicast socket and binds it to any available port
|
||||
* on the local host machine. The socket will be bound to the
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1995, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1995, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -27,6 +27,7 @@ package java.net;
|
|||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
@ -481,7 +482,6 @@ public class ServerSocket implements java.io.Closeable {
|
|||
* @see SecurityManager#checkConnect
|
||||
* @since 1.4
|
||||
*/
|
||||
|
||||
public SocketAddress getLocalSocketAddress() {
|
||||
if (!isBound())
|
||||
return null;
|
||||
|
@ -492,7 +492,23 @@ public class ServerSocket implements java.io.Closeable {
|
|||
* Listens for a connection to be made to this socket and accepts
|
||||
* it. The method blocks until a connection is made.
|
||||
*
|
||||
* <p>A new Socket {@code s} is created and, if there
|
||||
* <p> This method is {@linkplain Thread#interrupt() interruptible} in the
|
||||
* following circumstances:
|
||||
* <ol>
|
||||
* <li> The socket is {@linkplain ServerSocketChannel#socket() associated}
|
||||
* with a {@link ServerSocketChannel ServerSocketChannel}. In that
|
||||
* case, interrupting a thread accepting a connection will close the
|
||||
* underlying channel and cause this method to throw {@link
|
||||
* java.nio.channels.ClosedByInterruptException} with the interrupt
|
||||
* status set.
|
||||
* <li> The socket uses the system-default socket implementation and a
|
||||
* {@linkplain Thread#isVirtual() virtual thread} is accepting a
|
||||
* connection. In that case, interrupting the virtual thread will
|
||||
* cause it to wakeup and close the socket. This method will then throw
|
||||
* {@code SocketException} with the interrupt status set.
|
||||
* </ol>
|
||||
*
|
||||
* <p> A new Socket {@code s} is created and, if there
|
||||
* is a security manager,
|
||||
* the security manager's {@code checkAccept} method is called
|
||||
* with {@code s.getInetAddress().getHostAddress()} and
|
||||
|
@ -670,7 +686,18 @@ public class ServerSocket implements java.io.Closeable {
|
|||
assert !(si instanceof DelegatingSocketImpl);
|
||||
|
||||
// accept a connection
|
||||
impl.accept(si);
|
||||
try {
|
||||
impl.accept(si);
|
||||
} catch (SocketTimeoutException e) {
|
||||
throw e;
|
||||
} catch (InterruptedIOException e) {
|
||||
Thread thread = Thread.currentThread();
|
||||
if (thread.isVirtual() && thread.isInterrupted()) {
|
||||
close();
|
||||
throw new SocketException("Closed by interrupt");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
// check permission, close SocketImpl/connection if denied
|
||||
@SuppressWarnings("removal")
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1995, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1995, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -28,10 +28,12 @@ package java.net;
|
|||
import sun.security.util.SecurityConstants;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.nio.channels.ClosedByInterruptException;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
@ -570,6 +572,21 @@ public class Socket implements java.io.Closeable {
|
|||
/**
|
||||
* Connects this socket to the server.
|
||||
*
|
||||
* <p> This method is {@linkplain Thread#interrupt() interruptible} in the
|
||||
* following circumstances:
|
||||
* <ol>
|
||||
* <li> The socket is {@linkplain SocketChannel#socket() associated} with
|
||||
* a {@link SocketChannel SocketChannel}.
|
||||
* In that case, interrupting a thread establishing a connection will
|
||||
* close the underlying channel and cause this method to throw
|
||||
* {@link ClosedByInterruptException} with the interrupt status set.
|
||||
* <li> The socket uses the system-default socket implementation and a
|
||||
* {@linkplain Thread#isVirtual() virtual thread} is establishing a
|
||||
* connection. In that case, interrupting the virtual thread will
|
||||
* cause it to wakeup and close the socket. This method will then throw
|
||||
* {@code SocketException} with the interrupt status set.
|
||||
* </ol>
|
||||
*
|
||||
* @param endpoint the {@code SocketAddress}
|
||||
* @throws IOException if an error occurs during the connection
|
||||
* @throws java.nio.channels.IllegalBlockingModeException
|
||||
|
@ -588,6 +605,21 @@ public class Socket implements java.io.Closeable {
|
|||
* A timeout of zero is interpreted as an infinite timeout. The connection
|
||||
* will then block until established or an error occurs.
|
||||
*
|
||||
* <p> This method is {@linkplain Thread#interrupt() interruptible} in the
|
||||
* following circumstances:
|
||||
* <ol>
|
||||
* <li> The socket is {@linkplain SocketChannel#socket() associated} with
|
||||
* a {@link SocketChannel SocketChannel}.
|
||||
* In that case, interrupting a thread establishing a connection will
|
||||
* close the underlying channel and cause this method to throw
|
||||
* {@link ClosedByInterruptException} with the interrupt status set.
|
||||
* <li> The socket uses the system-default socket implementation and a
|
||||
* {@linkplain Thread#isVirtual() virtual thread} is establishing a
|
||||
* connection. In that case, interrupting the virtual thread will
|
||||
* cause it to wakeup and close the socket. This method will then throw
|
||||
* {@code SocketException} with the interrupt status set.
|
||||
* </ol>
|
||||
*
|
||||
* @param endpoint the {@code SocketAddress}
|
||||
* @param timeout the timeout value to be used in milliseconds.
|
||||
* @throws IOException if an error occurs during the connection
|
||||
|
@ -630,7 +662,18 @@ public class Socket implements java.io.Closeable {
|
|||
}
|
||||
if (!created)
|
||||
createImpl(true);
|
||||
impl.connect(epoint, timeout);
|
||||
try {
|
||||
impl.connect(epoint, timeout);
|
||||
} catch (SocketTimeoutException e) {
|
||||
throw e;
|
||||
} catch (InterruptedIOException e) {
|
||||
Thread thread = Thread.currentThread();
|
||||
if (thread.isVirtual() && thread.isInterrupted()) {
|
||||
close();
|
||||
throw new SocketException("Closed by interrupt");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
connected = true;
|
||||
/*
|
||||
* If the socket was not bound before the connect, it is now because
|
||||
|
@ -886,6 +929,22 @@ public class Socket implements java.io.Closeable {
|
|||
* is in non-blocking mode then the input stream's {@code read} operations
|
||||
* will throw an {@link java.nio.channels.IllegalBlockingModeException}.
|
||||
*
|
||||
* <p> Reading from the input stream is {@linkplain Thread#interrupt()
|
||||
* interruptible} in the following circumstances:
|
||||
* <ol>
|
||||
* <li> The socket is {@linkplain SocketChannel#socket() associated} with
|
||||
* a {@link SocketChannel SocketChannel}.
|
||||
* In that case, interrupting a thread reading from the input stream
|
||||
* will close the underlying channel and cause the read method to
|
||||
* throw {@link ClosedByInterruptException} with the interrupt
|
||||
* status set.
|
||||
* <li> The socket uses the system-default socket implementation and a
|
||||
* {@linkplain Thread#isVirtual() virtual thread} is reading from the
|
||||
* input stream. In that case, interrupting the virtual thread will
|
||||
* cause it to wakeup and close the socket. The read method will then
|
||||
* throw {@code SocketException} with the interrupt status set.
|
||||
* </ol>
|
||||
*
|
||||
* <p>Under abnormal conditions the underlying connection may be
|
||||
* broken by the remote host or the network software (for example
|
||||
* a connection reset in the case of TCP connections). When a
|
||||
|
@ -950,7 +1009,6 @@ public class Socket implements java.io.Closeable {
|
|||
private static class SocketInputStream extends InputStream {
|
||||
private final Socket parent;
|
||||
private final InputStream in;
|
||||
|
||||
SocketInputStream(Socket parent, InputStream in) {
|
||||
this.parent = parent;
|
||||
this.in = in;
|
||||
|
@ -963,13 +1021,23 @@ public class Socket implements java.io.Closeable {
|
|||
}
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
return in.read(b, off, len);
|
||||
try {
|
||||
return in.read(b, off, len);
|
||||
} catch (SocketTimeoutException e) {
|
||||
throw e;
|
||||
} catch (InterruptedIOException e) {
|
||||
Thread thread = Thread.currentThread();
|
||||
if (thread.isVirtual() && thread.isInterrupted()) {
|
||||
close();
|
||||
throw new SocketException("Closed by interrupt");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
return in.available();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
parent.close();
|
||||
|
@ -985,6 +1053,22 @@ public class Socket implements java.io.Closeable {
|
|||
* operations will throw an {@link
|
||||
* java.nio.channels.IllegalBlockingModeException}.
|
||||
*
|
||||
* <p> Writing to the output stream is {@linkplain Thread#interrupt()
|
||||
* interruptible} in the following circumstances:
|
||||
* <ol>
|
||||
* <li> The socket is {@linkplain SocketChannel#socket() associated} with
|
||||
* a {@link SocketChannel SocketChannel}.
|
||||
* In that case, interrupting a thread writing to the output stream
|
||||
* will close the underlying channel and cause the write method to
|
||||
* throw {@link ClosedByInterruptException} with the interrupt status
|
||||
* set.
|
||||
* <li> The socket uses the system-default socket implementation and a
|
||||
* {@linkplain Thread#isVirtual() virtual thread} is writing to the
|
||||
* output stream. In that case, interrupting the virtual thread will
|
||||
* cause it to wakeup and close the socket. The write method will then
|
||||
* throw {@code SocketException} with the interrupt status set.
|
||||
* </ol>
|
||||
*
|
||||
* <p> Closing the returned {@link java.io.OutputStream OutputStream}
|
||||
* will close the associated socket.
|
||||
*
|
||||
|
@ -1032,9 +1116,17 @@ public class Socket implements java.io.Closeable {
|
|||
}
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
out.write(b, off, len);
|
||||
try {
|
||||
out.write(b, off, len);
|
||||
} catch (InterruptedIOException e) {
|
||||
Thread thread = Thread.currentThread();
|
||||
if (thread.isVirtual() && thread.isInterrupted()) {
|
||||
close();
|
||||
throw new SocketException("Closed by interrupt");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
parent.close();
|
||||
|
|
|
@ -45,6 +45,7 @@ import java.util.ServiceLoader;
|
|||
|
||||
import jdk.internal.access.JavaNetURLAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.ThreadTracker;
|
||||
import jdk.internal.misc.VM;
|
||||
import sun.net.util.IPAddressUtil;
|
||||
import sun.security.util.SecurityConstants;
|
||||
|
@ -1342,15 +1343,24 @@ public final class URL implements java.io.Serializable {
|
|||
};
|
||||
}
|
||||
|
||||
// Thread-local gate to prevent recursive provider lookups
|
||||
private static ThreadLocal<Object> gate = new ThreadLocal<>();
|
||||
private static class ThreadTrackHolder {
|
||||
static final ThreadTracker TRACKER = new ThreadTracker();
|
||||
}
|
||||
|
||||
private static Object tryBeginLookup() {
|
||||
return ThreadTrackHolder.TRACKER.tryBegin();
|
||||
}
|
||||
|
||||
private static void endLookup(Object key) {
|
||||
ThreadTrackHolder.TRACKER.end(key);
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
private static URLStreamHandler lookupViaProviders(final String protocol) {
|
||||
if (gate.get() != null)
|
||||
Object key = tryBeginLookup();
|
||||
if (key == null) {
|
||||
throw new Error("Circular loading of URL stream handler providers detected");
|
||||
|
||||
gate.set(gate);
|
||||
}
|
||||
try {
|
||||
return AccessController.doPrivileged(
|
||||
new PrivilegedAction<>() {
|
||||
|
@ -1366,7 +1376,7 @@ public final class URL implements java.io.Serializable {
|
|||
}
|
||||
});
|
||||
} finally {
|
||||
gate.set(null);
|
||||
endLookup(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,11 +25,11 @@
|
|||
|
||||
package java.nio;
|
||||
|
||||
import jdk.internal.misc.Unsafe;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import jdk.internal.misc.Blocker;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
|
||||
/* package */ class MappedMemoryUtils {
|
||||
|
||||
|
@ -96,10 +96,15 @@ import java.io.UncheckedIOException;
|
|||
} else {
|
||||
// force writeback via file descriptor
|
||||
long offset = mappingOffset(address, index);
|
||||
long mappingAddress = mappingAddress(address, offset, index);
|
||||
long mappingLength = mappingLength(offset, length);
|
||||
long comp = Blocker.begin();
|
||||
try {
|
||||
force0(fd, mappingAddress(address, offset, index), mappingLength(offset, length));
|
||||
force0(fd, mappingAddress, mappingLength);
|
||||
} catch (IOException cause) {
|
||||
throw new UncheckedIOException(cause);
|
||||
} finally {
|
||||
Blocker.end(comp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -76,7 +76,7 @@ public abstract class AbstractSelectableChannel
|
|||
// Lock for registration and configureBlocking operations
|
||||
private final Object regLock = new Object();
|
||||
|
||||
// True when non-blocking, need regLock to change;
|
||||
// True when the channel is configured non-blocking, need regLock to change;
|
||||
private volatile boolean nonBlocking;
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -25,6 +25,7 @@
|
|||
|
||||
package java.nio.charset;
|
||||
|
||||
import jdk.internal.misc.ThreadTracker;
|
||||
import jdk.internal.misc.VM;
|
||||
import sun.nio.cs.ThreadLocalCoders;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
|
@ -47,6 +48,7 @@ import java.util.ServiceLoader;
|
|||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -370,9 +372,17 @@ public abstract class Charset
|
|||
};
|
||||
}
|
||||
|
||||
// Thread-local gate to prevent recursive provider lookups
|
||||
private static ThreadLocal<ThreadLocal<?>> gate =
|
||||
new ThreadLocal<ThreadLocal<?>>();
|
||||
private static class ThreadTrackHolder {
|
||||
static final ThreadTracker TRACKER = new ThreadTracker();
|
||||
}
|
||||
|
||||
private static Object tryBeginLookup() {
|
||||
return ThreadTrackHolder.TRACKER.tryBegin();
|
||||
}
|
||||
|
||||
private static void endLookup(Object key) {
|
||||
ThreadTrackHolder.TRACKER.end(key);
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
private static Charset lookupViaProviders(final String charsetName) {
|
||||
|
@ -388,12 +398,12 @@ public abstract class Charset
|
|||
if (!VM.isBooted())
|
||||
return null;
|
||||
|
||||
if (gate.get() != null)
|
||||
Object key = tryBeginLookup();
|
||||
if (key == null) {
|
||||
// Avoid recursive provider lookups
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
gate.set(gate);
|
||||
|
||||
return AccessController.doPrivileged(
|
||||
new PrivilegedAction<>() {
|
||||
public Charset run() {
|
||||
|
@ -409,7 +419,7 @@ public abstract class Charset
|
|||
});
|
||||
|
||||
} finally {
|
||||
gate.set(null);
|
||||
endLookup(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -384,7 +384,7 @@ public class Exchanger<V> {
|
|||
else if (spins > 0) {
|
||||
h ^= h << 1; h ^= h >>> 3; h ^= h << 10; // xorshift
|
||||
if (h == 0) // initialize hash
|
||||
h = SPINS | (int)t.getId();
|
||||
h = SPINS | (int)t.threadId();
|
||||
else if (h < 0 && // approx 50% true
|
||||
(--spins & ((SPINS >>> 1) - 1)) == 0)
|
||||
Thread.yield(); // two yields per wait
|
||||
|
@ -489,7 +489,7 @@ public class Exchanger<V> {
|
|||
if (spins > 0) {
|
||||
h ^= h << 1; h ^= h >>> 3; h ^= h << 10;
|
||||
if (h == 0)
|
||||
h = SPINS | (int)t.getId();
|
||||
h = SPINS | (int)t.threadId();
|
||||
else if (h < 0 && (--spins & ((SPINS >>> 1) - 1)) == 0)
|
||||
Thread.yield();
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ import java.security.PrivilegedExceptionAction;
|
|||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import jdk.internal.javac.PreviewFeature;
|
||||
import sun.security.util.SecurityConstants;
|
||||
|
||||
/**
|
||||
|
@ -237,6 +238,43 @@ public class Executors {
|
|||
threadFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Executor that starts a new Thread for each task.
|
||||
* The number of threads created by the Executor is unbounded.
|
||||
*
|
||||
* <p> Invoking {@link Future#cancel(boolean) cancel(true)} on a {@link
|
||||
* Future Future} representing the pending result of a task submitted to
|
||||
* the Executor will {@link Thread#interrupt() interrupt} the thread
|
||||
* executing the task.
|
||||
*
|
||||
* @param threadFactory the factory to use when creating new threads
|
||||
* @return a new executor that creates a new Thread for each task
|
||||
* @throws NullPointerException if threadFactory is null
|
||||
* @since 19
|
||||
*/
|
||||
@PreviewFeature(feature = PreviewFeature.Feature.VIRTUAL_THREADS)
|
||||
public static ExecutorService newThreadPerTaskExecutor(ThreadFactory threadFactory) {
|
||||
return ThreadPerTaskExecutor.create(threadFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Executor that starts a new virtual Thread for each task.
|
||||
* The number of threads created by the Executor is unbounded.
|
||||
*
|
||||
* <p> This method is equivalent to invoking
|
||||
* {@link #newThreadPerTaskExecutor(ThreadFactory)} with a thread factory
|
||||
* that creates virtual threads.
|
||||
*
|
||||
* @return a new executor that creates a new virtual Thread for each task
|
||||
* @throws UnsupportedOperationException if preview features are not enabled
|
||||
* @since 19
|
||||
*/
|
||||
@PreviewFeature(feature = PreviewFeature.Feature.VIRTUAL_THREADS)
|
||||
public static ExecutorService newVirtualThreadPerTaskExecutor() {
|
||||
ThreadFactory factory = Thread.ofVirtual().factory();
|
||||
return newThreadPerTaskExecutor(factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a single-threaded executor that can schedule commands
|
||||
* to run after a given delay, or to execute periodically.
|
||||
|
|
|
@ -51,8 +51,10 @@ 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 jdk.internal.access.JavaUtilConcurrentFJPAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
//import jdk.internal.vm.SharedThreadContainer; // for loom
|
||||
import jdk.internal.vm.SharedThreadContainer;
|
||||
|
||||
/**
|
||||
* An {@link ExecutorService} for running {@link ForkJoinTask}s.
|
||||
|
@ -1512,7 +1514,7 @@ public class ForkJoinPool extends AbstractExecutorService {
|
|||
final ForkJoinWorkerThreadFactory factory;
|
||||
final UncaughtExceptionHandler ueh; // per-worker UEH
|
||||
final Predicate<? super ForkJoinPool> saturate;
|
||||
// final SharedThreadContainer container; // for loom
|
||||
final SharedThreadContainer container;
|
||||
|
||||
@jdk.internal.vm.annotation.Contended("fjpctl") // segregate
|
||||
volatile long ctl; // main pool control
|
||||
|
@ -1568,8 +1570,7 @@ public class ForkJoinPool extends AbstractExecutorService {
|
|||
try {
|
||||
if (runState >= 0 && // avoid construction if terminating
|
||||
fac != null && (wt = fac.newThread(this)) != null) {
|
||||
wt.start(); // replace with following line for loom
|
||||
// container.start(wt);
|
||||
container.start(wt);
|
||||
return true;
|
||||
}
|
||||
} catch (Throwable rex) {
|
||||
|
@ -2528,7 +2529,7 @@ public class ForkJoinPool extends AbstractExecutorService {
|
|||
if ((cond = termination) != null)
|
||||
cond.signalAll();
|
||||
lock.unlock();
|
||||
// container.close(); // for loom
|
||||
container.close();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -2721,7 +2722,7 @@ public class ForkJoinPool extends AbstractExecutorService {
|
|||
String pid = Integer.toString(getAndAddPoolIds(1) + 1);
|
||||
String name = "ForkJoinPool-" + pid;
|
||||
this.workerNamePrefix = name + "-worker-";
|
||||
// this.container = SharedThreadContainer.create(name); // for loom
|
||||
this.container = SharedThreadContainer.create(name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2773,7 +2774,7 @@ public class ForkJoinPool extends AbstractExecutorService {
|
|||
this.workerNamePrefix = null;
|
||||
this.registrationLock = new ReentrantLock();
|
||||
this.queues = new WorkQueue[size];
|
||||
// this.container = SharedThreadContainer.create("ForkJoinPool.commonPool"); // for loom
|
||||
this.container = SharedThreadContainer.create("ForkJoinPool.commonPool");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3662,6 +3663,32 @@ public class ForkJoinPool extends AbstractExecutorService {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes tryCompensate to create or re-activate a spare thread to
|
||||
* compensate for a thread that performs a blocking operation. When the
|
||||
* blocking operation is done then endCompensatedBlock must be invoked
|
||||
* with the value returned by this method to re-adjust the parallelism.
|
||||
*/
|
||||
private long beginCompensatedBlock() {
|
||||
for (;;) {
|
||||
int comp;
|
||||
if ((comp = tryCompensate(ctl, false)) >= 0) {
|
||||
return (comp == 0) ? 0L : RC_UNIT;
|
||||
} else {
|
||||
Thread.onSpinWait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-adjusts parallelism after a blocking operation completes.
|
||||
*/
|
||||
void endCompensatedBlock(long post) {
|
||||
if (post > 0) {
|
||||
getAndAddCtl(post);
|
||||
}
|
||||
}
|
||||
|
||||
/** ManagedBlock for external threads */
|
||||
private static void unmanagedBlock(ManagedBlocker blocker)
|
||||
throws InterruptedException {
|
||||
|
@ -3704,6 +3731,17 @@ public class ForkJoinPool extends AbstractExecutorService {
|
|||
AccessController.doPrivileged(new PrivilegedAction<>() {
|
||||
public ForkJoinPool run() {
|
||||
return new ForkJoinPool((byte)0); }});
|
||||
// allow access to non-public methods
|
||||
SharedSecrets.setJavaUtilConcurrentFJPAccess(
|
||||
new JavaUtilConcurrentFJPAccess() {
|
||||
@Override
|
||||
public long beginCompensatedBlock(ForkJoinPool pool) {
|
||||
return pool.beginCompensatedBlock();
|
||||
}
|
||||
public void endCompensatedBlock(ForkJoinPool pool, long post) {
|
||||
pool.endCompensatedBlock(post);
|
||||
}
|
||||
});
|
||||
Class<?> dep = LockSupport.class; // ensure loaded
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,16 +54,19 @@ package java.util.concurrent;
|
|||
* to known values before returning it.
|
||||
* @since 1.5
|
||||
* @author Doug Lea
|
||||
* @see Thread.Builder#factory()
|
||||
*/
|
||||
public interface ThreadFactory {
|
||||
|
||||
/**
|
||||
* Constructs a new {@code Thread}. Implementations may also initialize
|
||||
* priority, name, daemon status, {@code ThreadGroup}, etc.
|
||||
* Constructs a new unstarted {@code Thread} to run the given runnable.
|
||||
*
|
||||
* @param r a runnable to be executed by new thread instance
|
||||
* @return constructed thread, or {@code null} if the request to
|
||||
* create a thread is rejected
|
||||
*
|
||||
* @see <a href="../../lang/Thread.html#inheritance">Inheritance when
|
||||
* creating threads</a>
|
||||
*/
|
||||
Thread newThread(Runnable r);
|
||||
}
|
||||
|
|
|
@ -50,6 +50,8 @@ import java.util.random.RandomGenerator;
|
|||
import java.util.stream.DoubleStream;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.LongStream;
|
||||
import jdk.internal.access.JavaUtilConcurrentTLRAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.util.random.RandomSupport;
|
||||
import jdk.internal.util.random.RandomSupport.*;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
|
@ -218,7 +220,7 @@ public final class ThreadLocalRandom extends Random {
|
|||
final long nextSeed() {
|
||||
Thread t; long r; // read and update per-thread seed
|
||||
U.putLong(t = Thread.currentThread(), SEED,
|
||||
r = U.getLong(t, SEED) + (t.getId() << 1) + GOLDEN_GAMMA);
|
||||
r = U.getLong(t, SEED) + (t.threadId() << 1) + GOLDEN_GAMMA);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -399,6 +401,19 @@ public final class ThreadLocalRandom extends Random {
|
|||
= new AtomicLong(RandomSupport.mixMurmur64(System.currentTimeMillis()) ^
|
||||
RandomSupport.mixMurmur64(System.nanoTime()));
|
||||
|
||||
// used by ExtentLocal
|
||||
private static class Access {
|
||||
static {
|
||||
SharedSecrets.setJavaUtilConcurrentTLRAccess(
|
||||
new JavaUtilConcurrentTLRAccess() {
|
||||
public int nextSecondaryThreadLocalRandomSeed() {
|
||||
return nextSecondarySeed();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// at end of <clinit> to survive static initialization circularity
|
||||
static {
|
||||
String sec = VM.getSavedProperty("java.util.secureRandomSeed");
|
||||
|
|
|
@ -0,0 +1,601 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package java.util.concurrent;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.security.Permission;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
import java.util.stream.Stream;
|
||||
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
||||
import jdk.internal.access.JavaLangAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.vm.ThreadContainer;
|
||||
import jdk.internal.vm.ThreadContainers;
|
||||
|
||||
/**
|
||||
* An ExecutorService that starts a new thread for each task. The number of
|
||||
* threads is unbounded.
|
||||
*/
|
||||
class ThreadPerTaskExecutor extends ThreadContainer implements ExecutorService {
|
||||
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
|
||||
private static final Permission MODIFY_THREAD = new RuntimePermission("modifyThread");
|
||||
private static final VarHandle STATE;
|
||||
static {
|
||||
try {
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
STATE = l.findVarHandle(ThreadPerTaskExecutor.class, "state", int.class);
|
||||
} catch (Exception e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private final ThreadFactory factory;
|
||||
private final Set<Thread> threads = ConcurrentHashMap.newKeySet();
|
||||
private final CountDownLatch terminationSignal = new CountDownLatch(1);
|
||||
|
||||
// states: RUNNING -> SHUTDOWN -> TERMINATED
|
||||
private static final int RUNNING = 0;
|
||||
private static final int SHUTDOWN = 1;
|
||||
private static final int TERMINATED = 2;
|
||||
private volatile int state;
|
||||
|
||||
// the key for this container in the registry
|
||||
private volatile Object key;
|
||||
|
||||
private ThreadPerTaskExecutor(ThreadFactory factory) {
|
||||
super(/*shared*/ true);
|
||||
this.factory = Objects.requireNonNull(factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a thread-per-task executor that creates threads using the given factory.
|
||||
*/
|
||||
static ThreadPerTaskExecutor create(ThreadFactory factory) {
|
||||
var executor = new ThreadPerTaskExecutor(factory);
|
||||
// register it to allow discovery by serviceability tools
|
||||
executor.key = ThreadContainers.registerContainer(executor);
|
||||
return executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws SecurityException if there is a security manager set and it denies
|
||||
* RuntimePermission("modifyThread").
|
||||
*/
|
||||
@SuppressWarnings("removal")
|
||||
private void checkPermission() {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
sm.checkPermission(MODIFY_THREAD);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws RejectedExecutionException if the executor has been shutdown.
|
||||
*/
|
||||
private void ensureNotShutdown() {
|
||||
if (state >= SHUTDOWN) {
|
||||
// shutdown or terminated
|
||||
throw new RejectedExecutionException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to terminate if already shutdown. If this method terminates the
|
||||
* executor then it signals any threads that are waiting for termination.
|
||||
*/
|
||||
private void tryTerminate() {
|
||||
assert state >= SHUTDOWN;
|
||||
if (threads.isEmpty()
|
||||
&& STATE.compareAndSet(this, SHUTDOWN, TERMINATED)) {
|
||||
|
||||
// signal waiters
|
||||
terminationSignal.countDown();
|
||||
|
||||
// remove from registry
|
||||
ThreadContainers.deregisterContainer(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to shutdown and terminate the executor.
|
||||
* If interruptThreads is true then all running threads are interrupted.
|
||||
*/
|
||||
private void tryShutdownAndTerminate(boolean interruptThreads) {
|
||||
if (STATE.compareAndSet(this, RUNNING, SHUTDOWN))
|
||||
tryTerminate();
|
||||
if (interruptThreads) {
|
||||
threads.forEach(Thread::interrupt);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Thread> threads() {
|
||||
return threads.stream().filter(Thread::isAlive);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long threadCount() {
|
||||
return threads.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
checkPermission();
|
||||
if (!isShutdown())
|
||||
tryShutdownAndTerminate(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Runnable> shutdownNow() {
|
||||
checkPermission();
|
||||
if (!isTerminated())
|
||||
tryShutdownAndTerminate(true);
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShutdown() {
|
||||
return state >= SHUTDOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminated() {
|
||||
return state >= TERMINATED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
|
||||
Objects.requireNonNull(unit);
|
||||
if (isTerminated()) {
|
||||
return true;
|
||||
} else {
|
||||
return terminationSignal.await(timeout, unit);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for executor to terminate.
|
||||
*/
|
||||
private void awaitTermination() {
|
||||
boolean terminated = isTerminated();
|
||||
if (!terminated) {
|
||||
tryShutdownAndTerminate(false);
|
||||
boolean interrupted = false;
|
||||
while (!terminated) {
|
||||
try {
|
||||
terminated = awaitTermination(1L, TimeUnit.DAYS);
|
||||
} catch (InterruptedException e) {
|
||||
if (!interrupted) {
|
||||
tryShutdownAndTerminate(true);
|
||||
interrupted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (interrupted) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
checkPermission();
|
||||
awaitTermination();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a thread to run the given task.
|
||||
*/
|
||||
private Thread newThread(Runnable task) {
|
||||
Thread thread = factory.newThread(task);
|
||||
if (thread == null)
|
||||
throw new RejectedExecutionException();
|
||||
return thread;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the executor that the task executed by the given thread is complete.
|
||||
* If the executor has been shutdown then this method will attempt to terminate
|
||||
* the executor.
|
||||
*/
|
||||
private void taskComplete(Thread thread) {
|
||||
boolean removed = threads.remove(thread);
|
||||
assert removed;
|
||||
if (state == SHUTDOWN) {
|
||||
tryTerminate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a thread to the set of threads and starts it.
|
||||
* @throws RejectedExecutionException
|
||||
*/
|
||||
private void start(Thread thread) {
|
||||
assert thread.getState() == Thread.State.NEW;
|
||||
threads.add(thread);
|
||||
|
||||
boolean started = false;
|
||||
try {
|
||||
if (state == RUNNING) {
|
||||
JLA.start(thread, this);
|
||||
started = true;
|
||||
}
|
||||
} finally {
|
||||
if (!started) {
|
||||
taskComplete(thread);
|
||||
}
|
||||
}
|
||||
|
||||
// throw REE if thread not started and no exception thrown
|
||||
if (!started) {
|
||||
throw new RejectedExecutionException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a thread to execute the given task.
|
||||
* @throws RejectedExecutionException
|
||||
*/
|
||||
private Thread start(Runnable task) {
|
||||
Objects.requireNonNull(task);
|
||||
ensureNotShutdown();
|
||||
Thread thread = newThread(new TaskRunner(this, task));
|
||||
start(thread);
|
||||
return thread;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Runnable task) {
|
||||
start(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Future<T> submit(Callable<T> task) {
|
||||
Objects.requireNonNull(task);
|
||||
ensureNotShutdown();
|
||||
var future = new ThreadBoundFuture<>(this, task);
|
||||
Thread thread = future.thread();
|
||||
start(thread);
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<?> submit(Runnable task) {
|
||||
return submit(Executors.callable(task));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Future<T> submit(Runnable task, T result) {
|
||||
return submit(Executors.callable(task, result));
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a task and notifies the executor when it completes.
|
||||
*/
|
||||
private static class TaskRunner implements Runnable {
|
||||
final ThreadPerTaskExecutor executor;
|
||||
final Runnable task;
|
||||
TaskRunner(ThreadPerTaskExecutor executor, Runnable task) {
|
||||
this.executor = executor;
|
||||
this.task = task;
|
||||
}
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
task.run();
|
||||
} finally {
|
||||
executor.taskComplete(Thread.currentThread());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Future for a task that runs in its own thread. The thread is
|
||||
* created (but not started) when the Future is created. The thread
|
||||
* is interrupted when the future is cancelled. The executor is
|
||||
* notified when the task completes.
|
||||
*/
|
||||
private static class ThreadBoundFuture<T>
|
||||
extends CompletableFuture<T> implements Runnable {
|
||||
|
||||
final ThreadPerTaskExecutor executor;
|
||||
final Callable<T> task;
|
||||
final Thread thread;
|
||||
|
||||
ThreadBoundFuture(ThreadPerTaskExecutor executor, Callable<T> task) {
|
||||
this.executor = executor;
|
||||
this.task = task;
|
||||
this.thread = executor.newThread(this);
|
||||
}
|
||||
|
||||
Thread thread() {
|
||||
return thread;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (Thread.currentThread() != thread) {
|
||||
// should not happen except where something casts this object
|
||||
// to a Runnable and invokes the run method.
|
||||
throw new WrongThreadException();
|
||||
}
|
||||
try {
|
||||
T result = task.call();
|
||||
complete(result);
|
||||
} catch (Throwable e) {
|
||||
completeExceptionally(e);
|
||||
} finally {
|
||||
executor.taskComplete(thread);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
boolean cancelled = super.cancel(mayInterruptIfRunning);
|
||||
if (cancelled && mayInterruptIfRunning)
|
||||
thread.interrupt();
|
||||
return cancelled;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
|
||||
throws InterruptedException {
|
||||
|
||||
Objects.requireNonNull(tasks);
|
||||
List<Future<T>> futures = new ArrayList<>();
|
||||
int j = 0;
|
||||
try {
|
||||
for (Callable<T> t : tasks) {
|
||||
Future<T> f = submit(t);
|
||||
futures.add(f);
|
||||
}
|
||||
for (int size = futures.size(); j < size; j++) {
|
||||
Future<T> f = futures.get(j);
|
||||
if (!f.isDone()) {
|
||||
try {
|
||||
f.get();
|
||||
} catch (ExecutionException | CancellationException ignore) { }
|
||||
}
|
||||
}
|
||||
return futures;
|
||||
} finally {
|
||||
cancelAll(futures, j);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
|
||||
long timeout, TimeUnit unit)
|
||||
throws InterruptedException {
|
||||
|
||||
Objects.requireNonNull(tasks);
|
||||
long deadline = System.nanoTime() + unit.toNanos(timeout);
|
||||
List<Future<T>> futures = new ArrayList<>();
|
||||
int j = 0;
|
||||
try {
|
||||
for (Callable<T> t : tasks) {
|
||||
Future<T> f = submit(t);
|
||||
futures.add(f);
|
||||
}
|
||||
for (int size = futures.size(); j < size; j++) {
|
||||
Future<T> f = futures.get(j);
|
||||
if (!f.isDone()) {
|
||||
try {
|
||||
f.get(deadline - System.nanoTime(), NANOSECONDS);
|
||||
} catch (TimeoutException e) {
|
||||
break;
|
||||
} catch (ExecutionException | CancellationException ignore) { }
|
||||
}
|
||||
}
|
||||
return futures;
|
||||
} finally {
|
||||
cancelAll(futures, j);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> void cancelAll(List<Future<T>> futures, int j) {
|
||||
for (int size = futures.size(); j < size; j++)
|
||||
futures.get(j).cancel(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
|
||||
throws InterruptedException, ExecutionException {
|
||||
try {
|
||||
return invokeAny(tasks, false, 0, null);
|
||||
} catch (TimeoutException e) {
|
||||
// should not happen
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
Objects.requireNonNull(unit);
|
||||
return invokeAny(tasks, true, timeout, unit);
|
||||
}
|
||||
|
||||
private <T> T invokeAny(Collection<? extends Callable<T>> tasks,
|
||||
boolean timed,
|
||||
long timeout,
|
||||
TimeUnit unit)
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
|
||||
int size = tasks.size();
|
||||
if (size == 0) {
|
||||
throw new IllegalArgumentException("'tasks' is empty");
|
||||
}
|
||||
|
||||
var holder = new AnyResultHolder<T>(Thread.currentThread());
|
||||
var threadList = new ArrayList<Thread>(size);
|
||||
long nanos = (timed) ? unit.toNanos(timeout) : 0;
|
||||
long startNanos = (timed) ? System.nanoTime() : 0;
|
||||
|
||||
try {
|
||||
int count = 0;
|
||||
Iterator<? extends Callable<T>> iterator = tasks.iterator();
|
||||
while (count < size && iterator.hasNext()) {
|
||||
Callable<T> task = iterator.next();
|
||||
Objects.requireNonNull(task);
|
||||
Thread thread = start(() -> {
|
||||
try {
|
||||
T r = task.call();
|
||||
holder.complete(r);
|
||||
} catch (Throwable e) {
|
||||
holder.completeExceptionally(e);
|
||||
}
|
||||
});
|
||||
threadList.add(thread);
|
||||
count++;
|
||||
}
|
||||
if (count == 0) {
|
||||
throw new IllegalArgumentException("'tasks' is empty");
|
||||
}
|
||||
|
||||
if (Thread.interrupted())
|
||||
throw new InterruptedException();
|
||||
T result = holder.result();
|
||||
while (result == null && holder.exceptionCount() < count) {
|
||||
if (timed) {
|
||||
long remainingNanos = nanos - (System.nanoTime() - startNanos);
|
||||
if (remainingNanos <= 0)
|
||||
throw new TimeoutException();
|
||||
LockSupport.parkNanos(remainingNanos);
|
||||
} else {
|
||||
LockSupport.park();
|
||||
}
|
||||
if (Thread.interrupted())
|
||||
throw new InterruptedException();
|
||||
result = holder.result();
|
||||
}
|
||||
|
||||
if (result != null) {
|
||||
return (result != AnyResultHolder.NULL) ? result : null;
|
||||
} else {
|
||||
throw new ExecutionException(holder.firstException());
|
||||
}
|
||||
|
||||
} finally {
|
||||
// interrupt any threads that are still running
|
||||
for (Thread t : threadList) {
|
||||
if (t.isAlive()) {
|
||||
t.interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An object for use by invokeAny to hold the result of the first task
|
||||
* to complete normally and/or the first exception thrown. The object
|
||||
* also maintains a count of the number of tasks that attempted to
|
||||
* complete up to when the first tasks completes normally.
|
||||
*/
|
||||
private static class AnyResultHolder<T> {
|
||||
private static final VarHandle RESULT;
|
||||
private static final VarHandle EXCEPTION;
|
||||
private static final VarHandle EXCEPTION_COUNT;
|
||||
static {
|
||||
try {
|
||||
MethodHandles.Lookup l = MethodHandles.lookup();
|
||||
RESULT = l.findVarHandle(AnyResultHolder.class, "result", Object.class);
|
||||
EXCEPTION = l.findVarHandle(AnyResultHolder.class, "exception", Throwable.class);
|
||||
EXCEPTION_COUNT = l.findVarHandle(AnyResultHolder.class, "exceptionCount", int.class);
|
||||
} catch (Exception e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
private static final Object NULL = new Object();
|
||||
|
||||
private final Thread owner;
|
||||
private volatile T result;
|
||||
private volatile Throwable exception;
|
||||
private volatile int exceptionCount;
|
||||
|
||||
AnyResultHolder(Thread owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete with the given result if not already completed. The winner
|
||||
* unparks the owner thread.
|
||||
*/
|
||||
void complete(T value) {
|
||||
@SuppressWarnings("unchecked")
|
||||
T v = (value != null) ? value : (T) NULL;
|
||||
if (result == null && RESULT.compareAndSet(this, null, v)) {
|
||||
LockSupport.unpark(owner);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete with the given exception. If the result is not already
|
||||
* set then it unparks the owner thread.
|
||||
*/
|
||||
void completeExceptionally(Throwable exc) {
|
||||
if (result == null) {
|
||||
if (exception == null)
|
||||
EXCEPTION.compareAndSet(this, null, exc);
|
||||
EXCEPTION_COUNT.getAndAdd(this, 1);
|
||||
LockSupport.unpark(owner);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns non-null if a task completed successfully. The result is
|
||||
* NULL if completed with null.
|
||||
*/
|
||||
T result() {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first exception thrown if recorded by this object.
|
||||
*
|
||||
* @apiNote The result() method should be used to test if there is
|
||||
* a result before invoking the exception method.
|
||||
*/
|
||||
Throwable firstException() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of tasks that terminated with an exception before
|
||||
* a task completed normally.
|
||||
*/
|
||||
int exceptionCount() {
|
||||
return exceptionCount;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -40,10 +40,12 @@ import java.util.ConcurrentModificationException;
|
|||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import jdk.internal.vm.SharedThreadContainer;
|
||||
|
||||
/**
|
||||
* An {@link ExecutorService} that executes each submitted task using
|
||||
|
@ -477,6 +479,11 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
|
|||
*/
|
||||
private final Condition termination = mainLock.newCondition();
|
||||
|
||||
/**
|
||||
* The thread container for the worker threads.
|
||||
*/
|
||||
private final SharedThreadContainer container;
|
||||
|
||||
/**
|
||||
* Tracks largest attained pool size. Accessed only under
|
||||
* mainLock.
|
||||
|
@ -726,6 +733,7 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
|
|||
} finally {
|
||||
ctl.set(ctlOf(TERMINATED, 0));
|
||||
termination.signalAll();
|
||||
container.close();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -942,7 +950,7 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
|
|||
mainLock.unlock();
|
||||
}
|
||||
if (workerAdded) {
|
||||
t.start();
|
||||
container.start(t);
|
||||
workerStarted = true;
|
||||
}
|
||||
}
|
||||
|
@ -1309,6 +1317,9 @@ public class ThreadPoolExecutor extends AbstractExecutorService {
|
|||
this.keepAliveTime = unit.toNanos(keepAliveTime);
|
||||
this.threadFactory = threadFactory;
|
||||
this.handler = handler;
|
||||
|
||||
String name = Objects.toIdentityString(this);
|
||||
this.container = SharedThreadContainer.create(name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
|
||||
package java.util.concurrent.locks;
|
||||
|
||||
import jdk.internal.misc.VirtualThreads;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
|
||||
/**
|
||||
|
@ -173,8 +174,13 @@ public class LockSupport {
|
|||
* this operation has no effect
|
||||
*/
|
||||
public static void unpark(Thread thread) {
|
||||
if (thread != null)
|
||||
U.unpark(thread);
|
||||
if (thread != null) {
|
||||
if (thread.isVirtual()) {
|
||||
VirtualThreads.unpark(thread);
|
||||
} else {
|
||||
U.unpark(thread);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -208,8 +214,15 @@ public class LockSupport {
|
|||
public static void park(Object blocker) {
|
||||
Thread t = Thread.currentThread();
|
||||
setBlocker(t, blocker);
|
||||
U.park(false, 0L);
|
||||
setBlocker(t, null);
|
||||
try {
|
||||
if (t.isVirtual()) {
|
||||
VirtualThreads.park();
|
||||
} else {
|
||||
U.park(false, 0L);
|
||||
}
|
||||
} finally {
|
||||
setBlocker(t, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -249,8 +262,15 @@ public class LockSupport {
|
|||
if (nanos > 0) {
|
||||
Thread t = Thread.currentThread();
|
||||
setBlocker(t, blocker);
|
||||
U.park(false, nanos);
|
||||
setBlocker(t, null);
|
||||
try {
|
||||
if (t.isVirtual()) {
|
||||
VirtualThreads.park(nanos);
|
||||
} else {
|
||||
U.park(false, nanos);
|
||||
}
|
||||
} finally {
|
||||
setBlocker(t, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -290,8 +310,15 @@ public class LockSupport {
|
|||
public static void parkUntil(Object blocker, long deadline) {
|
||||
Thread t = Thread.currentThread();
|
||||
setBlocker(t, blocker);
|
||||
U.park(true, deadline);
|
||||
setBlocker(t, null);
|
||||
try {
|
||||
if (t.isVirtual()) {
|
||||
VirtualThreads.parkUntil(deadline);
|
||||
} else {
|
||||
U.park(true, deadline);
|
||||
}
|
||||
} finally {
|
||||
setBlocker(t, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -338,7 +365,11 @@ public class LockSupport {
|
|||
* for example, the interrupt status of the thread upon return.
|
||||
*/
|
||||
public static void park() {
|
||||
U.park(false, 0L);
|
||||
if (Thread.currentThread().isVirtual()) {
|
||||
VirtualThreads.park();
|
||||
} else {
|
||||
U.park(false, 0L);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -372,8 +403,13 @@ public class LockSupport {
|
|||
* @param nanos the maximum number of nanoseconds to wait
|
||||
*/
|
||||
public static void parkNanos(long nanos) {
|
||||
if (nanos > 0)
|
||||
U.park(false, nanos);
|
||||
if (nanos > 0) {
|
||||
if (Thread.currentThread().isVirtual()) {
|
||||
VirtualThreads.park(nanos);
|
||||
} else {
|
||||
U.park(false, nanos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -407,24 +443,23 @@ public class LockSupport {
|
|||
* to wait until
|
||||
*/
|
||||
public static void parkUntil(long deadline) {
|
||||
U.park(true, deadline);
|
||||
if (Thread.currentThread().isVirtual()) {
|
||||
VirtualThreads.parkUntil(deadline);
|
||||
} else {
|
||||
U.park(true, deadline);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the thread id for the given thread. We must access
|
||||
* this directly rather than via method Thread.getId() because
|
||||
* getId() has been known to be overridden in ways that do not
|
||||
* preserve unique mappings.
|
||||
* Returns the thread id for the given thread.
|
||||
*/
|
||||
static final long getThreadId(Thread thread) {
|
||||
return U.getLong(thread, TID);
|
||||
return thread.threadId();
|
||||
}
|
||||
|
||||
// Hotspot implementation via intrinsics API
|
||||
private static final Unsafe U = Unsafe.getUnsafe();
|
||||
private static final long PARKBLOCKER
|
||||
= U.objectFieldOffset(Thread.class, "parkBlocker");
|
||||
private static final long TID
|
||||
= U.objectFieldOffset(Thread.class, "tid");
|
||||
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ package java.util.jar;
|
|||
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.access.JavaUtilZipFileAccess;
|
||||
import jdk.internal.misc.ThreadTracker;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
import sun.security.util.ManifestEntryVerifier;
|
||||
|
||||
|
@ -44,10 +45,8 @@ import java.util.ArrayList;
|
|||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipException;
|
||||
|
@ -150,7 +149,6 @@ public class JarFile extends ZipFile {
|
|||
private static final Runtime.Version RUNTIME_VERSION;
|
||||
private static final boolean MULTI_RELEASE_ENABLED;
|
||||
private static final boolean MULTI_RELEASE_FORCED;
|
||||
private static final ThreadLocal<Boolean> isInitializing = new ThreadLocal<>();
|
||||
// The maximum size of array to allocate. Some VMs reserve some header words in an array.
|
||||
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
|
||||
|
||||
|
@ -1036,6 +1034,18 @@ public class JarFile extends ZipFile {
|
|||
}
|
||||
}
|
||||
|
||||
private static class ThreadTrackHolder {
|
||||
static final ThreadTracker TRACKER = new ThreadTracker();
|
||||
}
|
||||
|
||||
private static Object beginInit() {
|
||||
return ThreadTrackHolder.TRACKER.begin();
|
||||
}
|
||||
|
||||
private static void endInit(Object key) {
|
||||
ThreadTrackHolder.TRACKER.end(key);
|
||||
}
|
||||
|
||||
synchronized void ensureInitialization() {
|
||||
try {
|
||||
maybeInstantiateVerifier();
|
||||
|
@ -1043,19 +1053,18 @@ public class JarFile extends ZipFile {
|
|||
throw new RuntimeException(e);
|
||||
}
|
||||
if (jv != null && !jvInitialized) {
|
||||
isInitializing.set(Boolean.TRUE);
|
||||
Object key = beginInit();
|
||||
try {
|
||||
initializeVerifier();
|
||||
jvInitialized = true;
|
||||
} finally {
|
||||
isInitializing.set(Boolean.FALSE);
|
||||
endInit(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isInitializing() {
|
||||
Boolean value = isInitializing.get();
|
||||
return (value == null) ? false : value;
|
||||
return ThreadTrackHolder.TRACKER.contains(Thread.currentThread());
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue