|
18 | 18 | package org.apache.commons.lang3.concurrent; |
19 | 19 |
|
20 | 20 | import static org.junit.jupiter.api.Assertions.assertEquals; |
| 21 | +import static org.junit.jupiter.api.Assertions.assertInstanceOf; |
21 | 22 | import static org.junit.jupiter.api.Assertions.assertThrows; |
| 23 | +import static org.junit.jupiter.api.Assertions.assertTrue; |
22 | 24 |
|
23 | 25 | import java.util.Arrays; |
24 | 26 | import java.util.List; |
| 27 | +import java.util.concurrent.CountDownLatch; |
25 | 28 | import java.util.concurrent.ExecutionException; |
26 | 29 | import java.util.concurrent.Future; |
27 | 30 | import java.util.concurrent.TimeUnit; |
28 | 31 | import java.util.concurrent.TimeoutException; |
| 32 | +import java.util.concurrent.atomic.AtomicBoolean; |
| 33 | +import java.util.concurrent.atomic.AtomicReference; |
| 34 | +import java.util.function.Consumer; |
29 | 35 | import java.util.stream.Collectors; |
30 | 36 |
|
31 | 37 | import org.apache.commons.lang3.AbstractLangTest; |
@@ -121,4 +127,62 @@ void testOnFuture() { |
121 | 127 | assertEquals("Z", UncheckedFuture.on(new TestFuture<>("Z")).get()); |
122 | 128 | } |
123 | 129 |
|
| 130 | + |
| 131 | + @Test |
| 132 | + void interruptFlagIsPreservedOnGet() throws Exception { |
| 133 | + assertInterruptPreserved(UncheckedFuture::get); |
| 134 | + } |
| 135 | + |
| 136 | + @Test |
| 137 | + void interruptFlagIsPreservedOnGetWithTimeout() throws Exception { |
| 138 | + assertInterruptPreserved(uf -> uf.get(1, TimeUnit.DAYS)); |
| 139 | + } |
| 140 | + |
| 141 | + private static void assertInterruptPreserved(Consumer<UncheckedFuture<Integer>> call) throws Exception { |
| 142 | + final CountDownLatch enteredGet = new CountDownLatch(1); |
| 143 | + final Future<Integer> blockingFuture = new AbstractFutureProxy<Integer>(ConcurrentUtils.constantFuture(42)) { |
| 144 | + private final CountDownLatch neverRelease = new CountDownLatch(1); |
| 145 | + |
| 146 | + @Override |
| 147 | + public Integer get() throws InterruptedException { |
| 148 | + enteredGet.countDown(); |
| 149 | + neverRelease.await(); |
| 150 | + throw new AssertionError("We should not get here"); |
| 151 | + } |
| 152 | + |
| 153 | + @Override |
| 154 | + public Integer get(long timeout, TimeUnit unit) throws InterruptedException { |
| 155 | + enteredGet.countDown(); |
| 156 | + neverRelease.await(); |
| 157 | + throw new AssertionError("We should not get here"); |
| 158 | + } |
| 159 | + |
| 160 | + @Override |
| 161 | + public boolean isDone() { |
| 162 | + return false; |
| 163 | + } |
| 164 | + |
| 165 | + }; |
| 166 | + final UncheckedFuture<Integer> uf = UncheckedFuture.on(blockingFuture); |
| 167 | + final AtomicReference<Throwable> thrown = new AtomicReference<>(); |
| 168 | + final AtomicBoolean interruptObserved = new AtomicBoolean(false); |
| 169 | + final Thread worker = new Thread(() -> { |
| 170 | + try { |
| 171 | + call.accept(uf); |
| 172 | + thrown.set(new AssertionError("We should not get here")); |
| 173 | + } catch (Throwable e) { |
| 174 | + interruptObserved.set(Thread.currentThread().isInterrupted()); |
| 175 | + thrown.set(e); |
| 176 | + } |
| 177 | + }, "unchecked-future-test-worker"); |
| 178 | + worker.start(); |
| 179 | + assertTrue(enteredGet.await(2, TimeUnit.SECONDS), "Worker did not enter Future.get() in time"); |
| 180 | + worker.interrupt(); |
| 181 | + worker.join(); |
| 182 | + final Throwable t = thrown.get(); |
| 183 | + assertInstanceOf(UncheckedInterruptedException.class, t, "Unexpected exception: " + t); |
| 184 | + assertInstanceOf(InterruptedException.class, t.getCause(), "Cause should be InterruptedException"); |
| 185 | + assertTrue(interruptObserved.get(), "Interrupt flag was not restored by the wrapper"); |
| 186 | + } |
| 187 | + |
124 | 188 | } |
0 commit comments