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:
Alan Bateman 2022-05-07 08:06:16 +00:00
parent 5212535a27
commit 9583e3657e
1133 changed files with 95935 additions and 8335 deletions

View file

@ -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");

View file

@ -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 &lt;= 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();
}

View file

@ -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;
}
}

View file

@ -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;
}
}
}

View file

@ -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;

View file

@ -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);
}
}
/**

View file

@ -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);
}
/**

View file

@ -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);
}
/**

View file

@ -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;
}
});
}
}

View file

@ -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;
}
});
}
}

View file

@ -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;
}
}
}

View file

@ -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

View file

@ -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.
*

View file

@ -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.
*

View file

@ -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) {

View file

@ -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);
}

View file

@ -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);
}
}

View file

@ -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

View 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);
}
}
}
}

View file

@ -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();
}

View file

@ -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();

View file

@ -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;
}
}
}

View file

@ -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
*/

View file

@ -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;
}
}

View file

@ -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

View 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

View file

@ -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

View file

@ -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;
}

File diff suppressed because it is too large Load diff

View file

@ -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);
}
}

View file

@ -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();
}
/**

View file

@ -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();
}
}
}

View file

@ -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();
}
}
}

View file

@ -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>();
}
});
}

View file

@ -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();
}
}
/**

View file

@ -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

View file

@ -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);
}
}
}

View file

@ -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

View file

@ -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")

View file

@ -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();

View file

@ -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);
}
}

View file

@ -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);
}
}
}

View file

@ -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;
/**

View file

@ -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);
}
}

View file

@ -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();
}

View file

@ -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.

View file

@ -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
}
}

View file

@ -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);
}

View file

@ -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");

View file

@ -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;
}
}
}

View file

@ -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);
}
/**

View file

@ -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");
}

View file

@ -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());
}
/*