Visitar URL original
fix: SmartExecutor may miss to finish the jobs (#2259) · apache/maven-resolver@bb91a66 · GitHub
Skip to content

Commit bb91a66

Browse files
authored
fix: SmartExecutor may miss to finish the jobs (#2259)
As it was catching Exception only, but it may throw other things as well, leading to lock-ups.
1 parent c889b5b commit bb91a66

2 files changed

Lines changed: 81 additions & 5 deletions

File tree

‎maven-resolver-util/src/main/java/org/eclipse/aether/util/concurrency/SmartExecutor.java‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public <T> CompletableFuture<T> submit(Callable<T> callable) {
6464
CompletableFuture<T> future = new CompletableFuture<>();
6565
try {
6666
future.complete(callable.call());
67-
} catch (Exception e) {
67+
} catch (Throwable e) {
6868
future.completeExceptionally(e);
6969
}
7070
return future;
@@ -118,7 +118,7 @@ public <T> Future<T> submit(Callable<T> callable) {
118118
Thread.currentThread().setContextClassLoader(tccl);
119119
try {
120120
future.complete(callable.call());
121-
} catch (Exception e) {
121+
} catch (Throwable e) {
122122
future.completeExceptionally(e);
123123
} finally {
124124
Thread.currentThread().setContextClassLoader(old);
@@ -127,7 +127,7 @@ public <T> Future<T> submit(Callable<T> callable) {
127127
} catch (RejectedExecutionException e) {
128128
try {
129129
future.complete(callable.call());
130-
} catch (Exception ex) {
130+
} catch (Throwable ex) {
131131
future.completeExceptionally(ex);
132132
}
133133
}
@@ -191,7 +191,7 @@ public <T> Future<T> submit(Callable<T> callable) {
191191
executor.submit(() -> {
192192
try {
193193
future.complete(callable.call());
194-
} catch (Exception e) {
194+
} catch (Throwable e) {
195195
future.completeExceptionally(e);
196196
} finally {
197197
semaphore.release();
@@ -200,7 +200,7 @@ public <T> Future<T> submit(Callable<T> callable) {
200200
} catch (RejectedExecutionException e) {
201201
try {
202202
future.complete(callable.call());
203-
} catch (Exception ex) {
203+
} catch (Throwable ex) {
204204
future.completeExceptionally(ex);
205205
} finally {
206206
semaphore.release();
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.eclipse.aether.util.concurrency;
20+
21+
import java.util.concurrent.Callable;
22+
import java.util.concurrent.ExecutionException;
23+
import java.util.concurrent.ExecutorService;
24+
import java.util.concurrent.Executors;
25+
import java.util.concurrent.Future;
26+
27+
import org.junit.jupiter.api.Test;
28+
29+
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
30+
import static org.junit.jupiter.api.Assertions.assertThrows;
31+
import static org.junit.jupiter.api.Assertions.assertTrue;
32+
33+
/**
34+
* Tests that {@link Error}s thrown by callables complete the future instead of leaving it hanging.
35+
*/
36+
class SmartExecutorTest {
37+
private static final Callable<String> BOOM = () -> {
38+
throw new AssertionError("boom");
39+
};
40+
41+
private static void assertErrorPropagated(SmartExecutor executor) {
42+
try (SmartExecutor ignored = executor) {
43+
Future<String> future = executor.submit(BOOM);
44+
ExecutionException e = assertThrows(ExecutionException.class, future::get);
45+
assertInstanceOf(AssertionError.class, e.getCause());
46+
assertTrue(future.isDone());
47+
}
48+
}
49+
50+
@Test
51+
void directPropagatesError() {
52+
assertErrorPropagated(new SmartExecutor.Direct());
53+
}
54+
55+
@Test
56+
void pooledPropagatesError() {
57+
ExecutorService pool = Executors.newFixedThreadPool(1);
58+
assertErrorPropagated(new SmartExecutor.Pooled(pool));
59+
}
60+
61+
@Test
62+
void limitedPropagatesError() {
63+
ExecutorService pool = Executors.newFixedThreadPool(1);
64+
assertErrorPropagated(new SmartExecutor.Limited(new SmartExecutor.Pooled(pool), 1));
65+
}
66+
67+
@Test
68+
void limitedReleasesPermitAfterError() throws Exception {
69+
ExecutorService pool = Executors.newFixedThreadPool(1);
70+
try (SmartExecutor executor = new SmartExecutor.Limited(new SmartExecutor.Pooled(pool), 1)) {
71+
assertThrows(ExecutionException.class, () -> executor.submit(BOOM).get());
72+
// would block forever if the permit leaked
73+
executor.submit(() -> "ok").get();
74+
}
75+
}
76+
}

0 commit comments

Comments
 (0)