getFromFuture will cancel the future by default on InterruptedException

Fixes #11339.

Closes #12453.

PiperOrigin-RevId: 341993215
diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteActionInputFetcher.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteActionInputFetcher.java
index 7f3f83d..93ec205 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/RemoteActionInputFetcher.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteActionInputFetcher.java
@@ -34,6 +34,7 @@
 import com.google.devtools.build.lib.remote.common.CacheNotFoundException;
 import com.google.devtools.build.lib.remote.util.DigestUtil;
 import com.google.devtools.build.lib.remote.util.TracingMetadataUtils;
+import com.google.devtools.build.lib.remote.util.Utils;
 import com.google.devtools.build.lib.sandbox.SandboxHelpers;
 import com.google.devtools.build.lib.vfs.Path;
 import io.grpc.Context;
@@ -42,7 +43,6 @@
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.ExecutionException;
 import javax.annotation.concurrent.GuardedBy;
 
 /**
@@ -145,14 +145,7 @@
 
   void downloadFile(Path path, FileArtifactValue metadata)
       throws IOException, InterruptedException {
-    try {
-      downloadFileAsync(path, metadata).get();
-    } catch (ExecutionException e) {
-      if (e.getCause() instanceof IOException) {
-        throw (IOException) e.getCause();
-      }
-      throw new IOException(e.getCause());
-    }
+    Utils.getFromFuture(downloadFileAsync(path, metadata));
   }
 
   private ListenableFuture<Void> downloadFileAsync(Path path, FileArtifactValue metadata)
diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteCache.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteCache.java
index 511c5bd..5fc3b3a 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/RemoteCache.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteCache.java
@@ -218,7 +218,7 @@
       try {
         if (interruptedException == null) {
           // Wait for all transfers to finish.
-          getFromFuture(transfer);
+          getFromFuture(transfer, cancelRemainingOnInterrupt);
         } else {
           transfer.cancel(true);
         }
diff --git a/src/main/java/com/google/devtools/build/lib/remote/util/Utils.java b/src/main/java/com/google/devtools/build/lib/remote/util/Utils.java
index 9d07d7a..86f21a5 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/util/Utils.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/util/Utils.java
@@ -57,9 +57,22 @@
   /**
    * Returns the result of a {@link ListenableFuture} if successful, or throws any checked {@link
    * Exception} directly if it's an {@link IOException} or else wraps it in an {@link IOException}.
+   *
+   * <p>Cancel the future on {@link InterruptedException}
    */
   public static <T> T getFromFuture(ListenableFuture<T> f)
       throws IOException, InterruptedException {
+    return getFromFuture(f, /* cancelOnInterrupt */ true);
+  }
+
+  /**
+   * Returns the result of a {@link ListenableFuture} if successful, or throws any checked {@link
+   * Exception} directly if it's an {@link IOException} or else wraps it in an {@link IOException}.
+   *
+   * @param cancelOnInterrupt cancel the future on {@link InterruptedException} if {@code true}.
+   */
+  public static <T> T getFromFuture(ListenableFuture<T> f, boolean cancelOnInterrupt)
+      throws IOException, InterruptedException {
     try {
       return f.get();
     } catch (ExecutionException e) {
@@ -74,6 +87,11 @@
         throw (RuntimeException) cause;
       }
       throw new IOException(cause);
+    } catch (InterruptedException e) {
+      if (cancelOnInterrupt) {
+        f.cancel(true);
+      }
+      throw e;
     }
   }