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