Skip to content

Commit

Permalink
A work-around JDK-8316882
Browse files Browse the repository at this point in the history
  • Loading branch information
modmuss50 committed Nov 10, 2024
1 parent 495aae3 commit 072ae30
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 1 deletion.
10 changes: 10 additions & 0 deletions src/main/java/net/fabricmc/loom/util/FileSystemUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ public String readString(String path) throws IOException {

@Override
public void close() throws IOException {
// JDK-8316882, a separate JDK bug where a zip FS cannot be closed on an interrupted thread
// Doing so will fail, after a stack trace is printed for each entry of the zip
// TODO is there a better way to know if the underlying file channel is closed?
if (Thread.currentThread().isInterrupted()) {
// We leak here as we never actually free the zip FS, but this is the best we can do
// Lets assume the JVM effectively fucked here, so throw a UnrecoverableZipException forcing it to exit
// When a build is canceled a new Gradle daemon will be started, so this is not a big deal
throw new UnrecoverableZipException("Cannot close zip FS on interrupted thread", new InterruptedException());
}

try {
reference.close();
} catch (IOException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ public static void tryStopGradleDaemon(Project project) {

@VisibleForTesting
public static boolean stopWhenIdle(Project project) {
// Clear the interrupted flag if set.
if (Thread.currentThread().isInterrupted()) {
assert Thread.interrupted();
}

DaemonInfo daemonInfo = findCurrentDaemon(project);

if (daemonInfo == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

package net.fabricmc.loom.test.unit

import java.nio.channels.ClosedChannelException
import java.nio.file.FileSystem
import java.nio.file.FileSystemAlreadyExistsException
import java.nio.file.FileSystemException
Expand All @@ -34,7 +35,7 @@ import java.nio.file.Path

import spock.lang.Specification

// Test to prove https://bugs.openjdk.org/browse/JDK-8291712
// Test to prove https://bugs.openjdk.org/browse/JDK-8291712 and https://bugs.openjdk.org/browse/JDK-8316882
// If this test starts failing on a new JDK, it is likely that the bug has been fixed!
class ClosedZipFSReproducer extends Specification {
def "JDK-8291712"() {
Expand Down Expand Up @@ -70,6 +71,33 @@ class ClosedZipFSReproducer extends Specification {
!fs.isOpen()
}

def "JDK-8316882"() {
when:
Path tempDir = Files.createTempDirectory("test")
Path zipFile = tempDir.resolve("example.zip")

// Create a new ZipFileSystem, and write a file to it
openZipFS(zipFile, true).withCloseable {
Files.writeString(it.getPath("test.txt"), "Hello, World!")
}

// Open the existing ZipFileSystem, interrupt the thread before reading the file
def fs = openZipFS(zipFile, false)

Thread.currentThread().interrupt()
Files.readString(fs.getPath("test.txt"))

// Close then unexpectedly throws ClosedChannelException
fs.close()

then:
thrown(ClosedChannelException)

// Reset the interrupt status
Thread.interrupted()
!Thread.currentThread().isInterrupted()
}

private static FileSystem openZipFS(Path path, boolean create) throws IOException {
URI uri = toJarUri(path)
try {
Expand Down
33 changes: 33 additions & 0 deletions src/test/groovy/net/fabricmc/loom/test/unit/ZipUtilsTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,39 @@ class ZipUtilsTest extends Specification {
thrown FileSystemUtil.UnrecoverableZipException
}

// Also see: ClosedZipFSReproducer
def "interrupted thread"() {
given:
def dir = File.createTempDir()
def zip = File.createTempFile("loom-zip-test", ".zip").toPath()
new File(dir, "test.json").text = """
{
"test": "This is a test of transforming"
}
"""
ZipUtils.pack(dir.toPath(), zip)

when:

ZipUtils.transformJson(JsonObject.class, zip, "test.json") { json ->
Thread.currentThread().interrupt()

json
}

then:
thrown FileSystemUtil.UnrecoverableZipException

// Reset the interrupt status
Thread.currentThread().isInterrupted()
Thread.interrupted()
!Thread.currentThread().isInterrupted()

cleanup:
// Cleanup after the mess we made.
FileSystemUtil.getJarFileSystem(zip, false).close()
}

def "reprocess uncompressed"() {
given:
// Create a reproducible input zip
Expand Down

0 comments on commit 072ae30

Please sign in to comment.