Skip to content

Commit bca3f91

Browse files
committed
interrup catcher
1 parent 840e3eb commit bca3f91

File tree

2 files changed

+113
-9
lines changed

2 files changed

+113
-9
lines changed

core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/JarTreeShakeInput.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -708,9 +708,19 @@ static class BytecodeSupplier implements Supplier<byte[]> {
708708

709709
@Override
710710
public byte[] get() {
711+
if (Thread.currentThread().isInterrupted()) {
712+
log.warnf(new Exception("interrupt stack trace"),
713+
"Thread %s is interrupted BEFORE ZipFS read of %s",
714+
Thread.currentThread().getName(), path);
715+
}
711716
try (InputStream is = Files.newInputStream(path)) {
712717
return is.readAllBytes();
713718
} catch (IOException e) {
719+
log.warnf(new Exception("read failure stack trace"),
720+
"Failed to read %s on thread %s (fileSystem.isOpen=%s, interrupted=%s)",
721+
path, Thread.currentThread().getName(),
722+
path.getFileSystem().isOpen(),
723+
Thread.currentThread().isInterrupted());
714724
throw new UncheckedIOException("Failed to read bytecode " + path
715725
+ " (fileSystem.isOpen=" + path.getFileSystem().isOpen() + ")", e);
716726
}

independent-projects/bootstrap/app-model/src/test/java/io/quarkus/paths/SharedArchivePathTreeTest.java

Lines changed: 103 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,20 +40,114 @@ void closedFileSystemException() throws IOException, InterruptedException, Execu
4040
});
4141
}
4242

43-
static void stress(Consumer<OpenPathTree> consumer) throws IOException {
44-
/* Find assertj-core jar in the class path */
43+
@Test
44+
void untrackedWalkDoesNotBreakSharedFs() throws IOException {
45+
/*
46+
* Tests that calling walk() on a SharedArchivePathTree (which inherits
47+
* ArchivePathTree.walk() and opens/closes a temporary ZipFS) does not
48+
* corrupt the shared ZipFS obtained via open().
49+
*/
50+
final Path jar = copyAssertjCoreJar();
51+
final ArchivePathTree tree = SharedArchivePathTree.forPath(jar);
52+
53+
// Step 1: open a shared ZipFS
54+
try (OpenPathTree shared = tree.open()) {
55+
// Step 2: call inherited walk() — opens and closes a temp ZipFS
56+
tree.walk(visit -> {
57+
// just iterate, don't need to do anything
58+
});
59+
60+
// Step 3: read from the shared ZipFS — should still work
61+
Path p = shared.getPath("org/assertj/core/api/Assertions.class");
62+
Assertions.assertThat(p).isNotNull();
63+
byte[] bytes = Files.readAllBytes(p);
64+
Assertions.assertThat(bytes).isNotEmpty();
65+
}
66+
}
67+
68+
@Test
69+
void untrackedWalkRawDoesNotBreakSharedFs() throws IOException {
70+
final Path jar = copyAssertjCoreJar();
71+
final ArchivePathTree tree = SharedArchivePathTree.forPath(jar);
72+
73+
try (OpenPathTree shared = tree.open()) {
74+
tree.walkRaw(visit -> {
75+
});
76+
77+
Path p = shared.getPath("org/assertj/core/api/Assertions.class");
78+
Assertions.assertThat(p).isNotNull();
79+
byte[] bytes = Files.readAllBytes(p);
80+
Assertions.assertThat(bytes).isNotEmpty();
81+
}
82+
}
83+
84+
@Test
85+
void multipleUntrackedWalksDoNotBreakSharedFs() throws IOException {
86+
final Path jar = copyAssertjCoreJar();
87+
final ArchivePathTree tree = SharedArchivePathTree.forPath(jar);
88+
89+
try (OpenPathTree shared = tree.open()) {
90+
// Multiple untracked walks
91+
for (int i = 0; i < 5; i++) {
92+
tree.walk(visit -> {
93+
});
94+
}
95+
96+
Path p = shared.getPath("org/assertj/core/api/Assertions.class");
97+
Assertions.assertThat(p).isNotNull();
98+
byte[] bytes = Files.readAllBytes(p);
99+
Assertions.assertThat(bytes).isNotEmpty();
100+
}
101+
}
102+
103+
@Test
104+
void concurrentUntrackedWalkDoesNotBreakSharedFs() throws IOException, InterruptedException, ExecutionException {
105+
final Path jar = copyAssertjCoreJar();
106+
final ArchivePathTree tree = SharedArchivePathTree.forPath(jar);
107+
108+
try (OpenPathTree shared = tree.open()) {
109+
// Concurrently walk (untracked) while holding the shared ZipFS open
110+
final ExecutorService executor = Executors.newFixedThreadPool(8);
111+
final List<Future<Void>> futures = new ArrayList<>(8);
112+
try {
113+
for (int i = 0; i < 8; i++) {
114+
futures.add(executor.submit(() -> {
115+
tree.walk(visit -> {
116+
});
117+
return null;
118+
}));
119+
}
120+
for (Future<Void> f : futures) {
121+
Assertions.assertThat(f).succeedsWithin(30, TimeUnit.SECONDS);
122+
}
123+
} finally {
124+
executor.shutdown();
125+
}
126+
127+
// The shared ZipFS should still be usable
128+
Path p = shared.getPath("org/assertj/core/api/Assertions.class");
129+
Assertions.assertThat(p).isNotNull();
130+
byte[] bytes = Files.readAllBytes(p);
131+
Assertions.assertThat(bytes).isNotEmpty();
132+
}
133+
}
134+
135+
private static Path copyAssertjCoreJar() throws IOException {
45136
final String rawCp = System.getProperty("java.class.path");
46137
final String assertjCoreJarPath = Stream.of(rawCp.split(System.getProperty("path.separator")))
47138
.filter(p -> p.contains("assertj-core"))
48139
.findFirst()
49-
.orElseThrow(() -> new AssertionError("Could not find assertj-core in " + rawCp));
50-
51-
/* Create a copy of assertj-core jar in target directory */
52-
final Path assertjCoreJarPathCopy = Path.of("target/assertj-core-" + UUID.randomUUID() + ".jar");
53-
if (!Files.exists(assertjCoreJarPathCopy.getParent())) {
54-
Files.createDirectories(assertjCoreJarPathCopy.getParent());
140+
.orElseThrow(() -> new AssertionError("Could not find assertj-core in " + rawCp));
141+
final Path copy = Path.of("target/assertj-core-" + UUID.randomUUID() + ".jar");
142+
if (!Files.exists(copy.getParent())) {
143+
Files.createDirectories(copy.getParent());
55144
}
56-
Files.copy(Path.of(assertjCoreJarPath), assertjCoreJarPathCopy);
145+
Files.copy(Path.of(assertjCoreJarPath), copy);
146+
return copy;
147+
}
148+
149+
static void stress(Consumer<OpenPathTree> consumer) throws IOException {
150+
final Path assertjCoreJarPathCopy = copyAssertjCoreJar();
57151

58152
/* Now do some concurrent opening and closing of the SharedArchivePathTree instance */
59153
final ArchivePathTree archivePathTree = SharedArchivePathTree.forPath(assertjCoreJarPathCopy);

0 commit comments

Comments
 (0)