mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-07-15 01:07:30 +08:00
Repair file channel when it's closed by interruption
When an interrupted that calls FileChannel.read, the channel is closed and the read fails with a ClosedByInterruptException. The closure of the channel makes it unusable by other threads. To allow other threads to read from the data block, this commit recreates the FileChannel when a read fails on an interrupted thread with a ClosedByInterruptException. The exception is then rethrown to continue the thread's interruption. Closes gh-38154
This commit is contained in:
parent
173e6543fd
commit
890a3e72ac
@ -18,6 +18,7 @@ package org.springframework.boot.loader.zip;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.ClosedByInterruptException;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.file.Files;
|
||||
@ -179,7 +180,13 @@ class FileChannelDataBlock implements CloseableDataBlock {
|
||||
synchronized (this.lock) {
|
||||
if (position < this.bufferPosition || position >= this.bufferPosition + this.bufferSize) {
|
||||
this.buffer.clear();
|
||||
this.bufferSize = this.fileChannel.read(this.buffer, position);
|
||||
try {
|
||||
this.bufferSize = this.fileChannel.read(this.buffer, position);
|
||||
}
|
||||
catch (ClosedByInterruptException ex) {
|
||||
repairFileChannel();
|
||||
throw ex;
|
||||
}
|
||||
this.bufferPosition = position;
|
||||
}
|
||||
if (this.bufferSize <= 0) {
|
||||
@ -193,6 +200,16 @@ class FileChannelDataBlock implements CloseableDataBlock {
|
||||
}
|
||||
}
|
||||
|
||||
private void repairFileChannel() throws IOException {
|
||||
if (tracker != null) {
|
||||
tracker.closedFileChannel(this.path, this.fileChannel);
|
||||
}
|
||||
this.fileChannel = FileChannel.open(this.path, StandardOpenOption.READ);
|
||||
if (tracker != null) {
|
||||
tracker.openedFileChannel(this.path, this.fileChannel);
|
||||
}
|
||||
}
|
||||
|
||||
void open() throws IOException {
|
||||
synchronized (this.lock) {
|
||||
if (this.referenceCount == 0) {
|
||||
@ -231,7 +248,7 @@ class FileChannelDataBlock implements CloseableDataBlock {
|
||||
|
||||
<E extends Exception> void ensureOpen(Supplier<E> exceptionSupplier) throws E {
|
||||
synchronized (this.lock) {
|
||||
if (this.referenceCount == 0) {
|
||||
if (this.referenceCount == 0 || !this.fileChannel.isOpen()) {
|
||||
throw exceptionSupplier.get();
|
||||
}
|
||||
}
|
||||
|
@ -19,9 +19,11 @@ package org.springframework.boot.loader.zip;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.ClosedByInterruptException;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@ -74,6 +76,28 @@ class FileChannelDataBlockTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void readReadsFileWhenAnotherThreadHasBeenInterrupted() throws IOException, InterruptedException {
|
||||
try (FileChannelDataBlock block = createAndOpenBlock()) {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(CONTENT.length);
|
||||
AtomicReference<IOException> failure = new AtomicReference<>();
|
||||
Thread thread = new Thread(() -> {
|
||||
Thread.currentThread().interrupt();
|
||||
try {
|
||||
block.read(ByteBuffer.allocate(CONTENT.length), 0);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
failure.set(ex);
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
thread.join();
|
||||
assertThat(failure.get()).isInstanceOf(ClosedByInterruptException.class);
|
||||
assertThat(block.read(buffer, 0)).isEqualTo(6);
|
||||
assertThat(buffer.array()).containsExactly(CONTENT);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void readDoesNotReadPastEndOfFile() throws IOException {
|
||||
try (FileChannelDataBlock block = createAndOpenBlock()) {
|
||||
|
Loading…
Reference in New Issue
Block a user