Skip to content

Commit 67fc1d8

Browse files
committed
ClosedFileSystemException or NullPointerException thrown when
SharedArchivePathTree is opened and closed concurrently, fix #48220
1 parent 55d17c1 commit 67fc1d8

File tree

2 files changed

+87
-2
lines changed

2 files changed

+87
-2
lines changed

independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/SharedArchivePathTree.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,11 @@ public OpenPathTree open() {
6060
}
6161
}
6262
try {
63-
this.lastOpen = new SharedOpenArchivePathTree(openFs());
63+
lastOpen = this.lastOpen = new SharedOpenArchivePathTree(openFs());
6464
} catch (IOException e) {
6565
throw new UncheckedIOException(e);
6666
}
67-
return new CallerOpenPathTree(this.lastOpen);
67+
return new CallerOpenPathTree(lastOpen);
6868
}
6969

7070
private class SharedOpenArchivePathTree extends OpenArchivePathTree {
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package io.quarkus.paths;
2+
3+
import java.io.IOException;
4+
import java.nio.file.Files;
5+
import java.nio.file.Path;
6+
import java.util.ArrayList;
7+
import java.util.List;
8+
import java.util.UUID;
9+
import java.util.concurrent.ExecutionException;
10+
import java.util.concurrent.ExecutorService;
11+
import java.util.concurrent.Executors;
12+
import java.util.concurrent.Future;
13+
import java.util.concurrent.TimeUnit;
14+
import java.util.function.Consumer;
15+
import java.util.stream.Stream;
16+
17+
import org.assertj.core.api.Assertions;
18+
import org.junit.jupiter.api.Test;
19+
20+
public class SharedArchivePathTreeTest {
21+
private static final int WORKERS_COUNT = 128;
22+
23+
@Test
24+
void nullPointerException() throws IOException, InterruptedException, ExecutionException {
25+
/* Reproduce https://github.com/quarkusio/quarkus/issues/48220 */
26+
stress((OpenPathTree opened) -> {
27+
});
28+
}
29+
30+
@Test
31+
void closedFileSystemException() throws IOException, InterruptedException, ExecutionException {
32+
/* Reproduce https://github.com/quarkusio/quarkus/issues/48220 */
33+
stress((OpenPathTree opened) -> {
34+
try {
35+
Path p = opened.getPath("org/assertj/core/api/Assertions.class");
36+
Files.readAllBytes(p);
37+
} catch (IOException e) {
38+
throw new RuntimeException(e);
39+
}
40+
});
41+
}
42+
43+
static void stress(Consumer<OpenPathTree> consumer) throws IOException {
44+
/* Find assertj-core jar in the class path */
45+
final String rawCp = System.getProperty("java.class.path");
46+
final String assertjCoreJarPath = Stream.of(rawCp.split(System.getProperty("path.separator")))
47+
.filter(p -> p.contains("assertj-core"))
48+
.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());
55+
}
56+
Files.copy(Path.of(assertjCoreJarPath), assertjCoreJarPathCopy);
57+
58+
/* Now do some concurrent opening and closing of the SharedArchivePathTree instance */
59+
final ArchivePathTree archivePathTree = SharedArchivePathTree.forPath(assertjCoreJarPathCopy);
60+
final ExecutorService executor = Executors.newFixedThreadPool(WORKERS_COUNT);
61+
final List<Future<Void>> futures = new ArrayList<>(WORKERS_COUNT);
62+
try {
63+
for (int i = 0; i < WORKERS_COUNT; i++) {
64+
final Future<Void> f = executor.submit(() -> {
65+
try (OpenPathTree opened = archivePathTree.open()) {
66+
consumer.accept(opened);
67+
}
68+
return null;
69+
});
70+
futures.add(f);
71+
}
72+
73+
// Ensure all tasks are completed
74+
int i = 0;
75+
for (Future<Void> future : futures) {
76+
Assertions.assertThat(future)
77+
.describedAs("Expected success at iteration %d", i++)
78+
.succeedsWithin(30, TimeUnit.SECONDS);
79+
}
80+
} finally {
81+
executor.shutdown();
82+
}
83+
}
84+
85+
}

0 commit comments

Comments
 (0)