org.gradle.util.internal.DisconnectableInputStream Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-tooling-api Show documentation
Show all versions of gradle-tooling-api Show documentation
Gradle Tooling API - the programmatic API to invoke Gradle
The newest version!
/*
* Copyright 2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.util.internal;
import org.gradle.api.Action;
import org.gradle.internal.UncheckedException;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* An {@code InputStream} which reads from the source {@code InputStream}. In addition, when the {@code InputStream} is
* closed, all threads blocked reading from the stream will receive an end-of-stream.
*/
public class DisconnectableInputStream extends BulkReadInputStream {
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
private final byte[] buffer;
private int readPos;
private int writePos;
private boolean closed;
private boolean inputFinished;
/*
The song and dance with Action is to ease testing.
See DisconnectableInputStreamTest
*/
static class ThreadExecuter implements Action {
@Override
public void execute(Runnable runnable) {
Thread thread = new Thread(runnable);
thread.setName("DisconnectableInputStream source reader");
thread.setDaemon(true);
thread.start();
}
}
public DisconnectableInputStream(InputStream source) {
this(source, 1024);
}
public DisconnectableInputStream(final InputStream source, int bufferLength) {
this(source, new ThreadExecuter(), bufferLength);
}
DisconnectableInputStream(InputStream source, Action executer) {
this(source, executer, 1024);
}
DisconnectableInputStream(final InputStream source, Action executer, int bufferLength) {
buffer = new byte[bufferLength];
Runnable consume = new Runnable() {
@Override
public void run() {
try {
while (true) {
int pos;
lock.lock();
try {
while (!closed && writePos == buffer.length && writePos != readPos) {
// buffer is full, wait until it has been read
condition.await();
}
assert writePos >= readPos;
if (closed) {
// stream has been closed, don't bother reading anything else
inputFinished = true;
condition.signalAll();
return;
}
if (readPos == writePos) {
// buffer has been fully read, start at the beginning
readPos = 0;
writePos = 0;
}
pos = writePos;
} finally {
lock.unlock();
}
int nread = source.read(buffer, pos, buffer.length - pos);
lock.lock();
try {
if (nread > 0) {
// Have read some data - let readers know
assert writePos >= readPos;
writePos += nread;
assert buffer.length >= writePos;
condition.signalAll();
}
if (nread < 0) {
// End of the stream
inputFinished = true;
condition.signalAll();
return;
}
} finally {
lock.unlock();
}
}
} catch (Throwable throwable) {
lock.lock();
try {
inputFinished = true;
condition.signalAll();
} finally {
lock.unlock();
}
throw UncheckedException.throwAsUncheckedException(throwable);
}
}
};
executer.execute(consume);
}
@Override
public int read(byte[] bytes, int pos, int count) throws IOException {
lock.lock();
try {
while (!inputFinished && !closed && readPos == writePos) {
condition.await();
}
if (closed) {
return -1;
}
// Drain the buffer before returning end-of-stream
if (writePos > readPos) {
int nread = Math.min(count, writePos - readPos);
System.arraycopy(buffer, readPos, bytes, pos, nread);
readPos += nread;
assert writePos >= readPos;
condition.signalAll();
return nread;
}
assert inputFinished;
return -1;
} catch (InterruptedException e) {
throw UncheckedException.throwAsUncheckedException(e);
} finally {
lock.unlock();
}
}
/**
* Closes this {@code InputStream} for reading. Any threads blocked in read() will receive an end-of-stream. Also requests that the
* reader thread stop reading, if possible, but does not block waiting for this to happen.
*
* NOTE: this method does not close the source input stream.
*/
@Override
public void close() throws IOException {
lock.lock();
try {
closed = true;
condition.signalAll();
} finally {
lock.unlock();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy