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:
Andy Wilkinson
2023-10-31 18:29:08 +00:00
parent 173e6543fd
commit 890a3e72ac
2 changed files with 43 additions and 2 deletions

View File

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