diff --git a/core/src/main/java/org/bitcoinj/base/internal/InternalUtils.java b/core/src/main/java/org/bitcoinj/base/internal/InternalUtils.java index f81ac47c5..42fcf6dde 100644 --- a/core/src/main/java/org/bitcoinj/base/internal/InternalUtils.java +++ b/core/src/main/java/org/bitcoinj/base/internal/InternalUtils.java @@ -19,6 +19,8 @@ package org.bitcoinj.base.internal; import java.util.Arrays; import java.util.List; import java.util.Objects; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; import java.util.stream.Collectors; /** @@ -93,4 +95,30 @@ public class InternalUtils { public static String commaJoin(String... strings) { return Arrays.stream(strings).filter(Objects::nonNull).collect(Collectors.joining(", ")); } + + /** + * Get a future's value uninterruptibly by temporarily ignoring {@link InterruptedException}, but making + * sure we re-set the thread's interrupt status, so higher-level code on the thread can handle the + * interruption properly. Based upon the Guava implementation. + * @param future future with value to get + * @param type of value + * @return the value + * @throws ExecutionException if the computation through an exception + */ + public static V getUninterruptibly(Future future) throws ExecutionException { + boolean interrupted = false; + try { + while (true) { + try { + return future.get(); + } catch (InterruptedException e) { + interrupted = true; + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } } diff --git a/core/src/main/java/org/bitcoinj/core/PeerGroup.java b/core/src/main/java/org/bitcoinj/core/PeerGroup.java index 85da43459..94bb9da1e 100644 --- a/core/src/main/java/org/bitcoinj/core/PeerGroup.java +++ b/core/src/main/java/org/bitcoinj/core/PeerGroup.java @@ -25,6 +25,7 @@ import com.google.common.util.concurrent.Runnables; import com.google.common.util.concurrent.Uninterruptibles; import net.jcip.annotations.GuardedBy; import org.bitcoinj.base.Network; +import org.bitcoinj.base.internal.InternalUtils; import org.bitcoinj.base.internal.PlatformUtils; import org.bitcoinj.base.internal.Stopwatch; import org.bitcoinj.base.internal.TimeUtils; @@ -1528,7 +1529,7 @@ public class PeerGroup implements TransactionBroadcaster { peers.size(), pendingPeers.size(), maxConnections); CompletableFuture future = channels.openConnection(address.toSocketAddress(), peer); if (future.isDone()) - Uninterruptibles.getUninterruptibly(future); + InternalUtils.getUninterruptibly(future); } catch (ExecutionException e) { Throwable cause = Throwables.getRootCause(e); log.warn("Failed to connect to " + address + ": " + cause.getMessage()); diff --git a/integration-test/src/test/java/org/bitcoinj/core/PeerTest.java b/integration-test/src/test/java/org/bitcoinj/core/PeerTest.java index bdb5bccb1..548bccd51 100644 --- a/integration-test/src/test/java/org/bitcoinj/core/PeerTest.java +++ b/integration-test/src/test/java/org/bitcoinj/core/PeerTest.java @@ -17,11 +17,11 @@ package org.bitcoinj.core; import com.google.common.collect.Lists; -import com.google.common.util.concurrent.Uninterruptibles; import org.bitcoinj.base.BitcoinNetwork; import org.bitcoinj.base.Coin; import org.bitcoinj.base.ScriptType; import org.bitcoinj.base.Sha256Hash; +import org.bitcoinj.base.internal.InternalUtils; import org.bitcoinj.base.internal.TimeUtils; import org.bitcoinj.core.listeners.BlocksDownloadedEventListener; import org.bitcoinj.core.listeners.PreMessageReceivedEventListener; @@ -779,8 +779,8 @@ public class PeerTest extends TestWithNetworkConnections { connectWithVersion(500, Services.NODE_NETWORK); // We must wait uninterruptibly here because connect[WithVersion] generates a peer that interrupts the current // thread when it disconnects. - Uninterruptibles.getUninterruptibly(connectedFuture); - Uninterruptibles.getUninterruptibly(disconnectedFuture); + InternalUtils.getUninterruptibly(connectedFuture); + InternalUtils.getUninterruptibly(disconnectedFuture); try { peer.writeTarget.writeBytes(new byte[1]); fail();