diff --git a/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/ChainingListenableFuture.java b/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/ChainingListenableFuture.java
index 1232d0fb..932a8c14 100644
--- a/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/ChainingListenableFuture.java
+++ b/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/ChainingListenableFuture.java
@@ -208,7 +208,7 @@ public void run() {
ChainingListenableFuture.this.outputFuture = null;
}
}
- }, MoreExecutors.sameThreadExecutor());
+ }, Futures.newExecutorService());
} catch (UndeclaredThrowableException e) {
// Set the cause of the exception as this future's exception
setException(e.getCause());
diff --git a/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/Futures.java b/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/Futures.java
index 8900e6de..9b313f24 100644
--- a/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/Futures.java
+++ b/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/Futures.java
@@ -26,9 +26,12 @@
import com.google.common.util.concurrent.SettableFuture;
import javax.annotation.Nullable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.util.concurrent.Uninterruptibles.getUninterruptibly;
@@ -65,7 +68,7 @@ public abstract class Futures {
* });}
*
* Note: This overload of {@code addCallback} is designed for cases in
- * which the callack is fast and lightweight, as the method does not accept
+ * which the callback is fast and lightweight, as the method does not accept
* an {@code Executor} in which to perform the the work. For heavier
* callbacks, this overload carries some caveats: First, the thread that the
* callback runs in depends on whether the input {@code Future} is done at the
@@ -74,7 +77,7 @@ public abstract class Futures {
* callback in the thread that calls {@code addCallback} or {@code
* Future.cancel}. Second, callbacks may run in an internal thread of the
* system responsible for the input {@code Future}, such as an RPC network
- * thread. Finally, during the execution of a {@code sameThreadExecutor}
+ * thread. Finally, during the execution of a {@code newExecutorService}
* callback, all other registered but unexecuted listeners are prevented from
* running, even if those listeners are to run in other executors.
*
@@ -87,7 +90,7 @@ public abstract class Futures {
*/
public static void addCallback(ListenableFuture future,
FutureCallback super V> callback) {
- addCallback(future, callback, MoreExecutors.sameThreadExecutor());
+ addCallback(future, callback, newExecutorService());
}
/**
@@ -115,8 +118,8 @@ public static void addCallback(ListenableFuture future,
*
* When the callback is fast and lightweight consider {@linkplain
* Futures#addCallback(ListenableFuture, FutureCallback) the other overload}
- * or explicit use of {@link MoreExecutors#sameThreadExecutor
- * sameThreadExecutor}. For heavier callbacks, this choice carries some
+ * or explicit use of {@link #newExecutorService()}.
+ * For heavier callbacks, this choice carries some
* caveats: First, the thread that the callback runs in depends on whether
* the input {@code Future} is done at the time {@code addCallback} is called
* and on whether the input {@code Future} is ever cancelled. In particular,
@@ -124,7 +127,7 @@ public static void addCallback(ListenableFuture future,
* {@code addCallback} or {@code Future.cancel}. Second, callbacks may run in
* an internal thread of the system responsible for the input {@code Future},
* such as an RPC network thread. Finally, during the execution of a {@code
- * sameThreadExecutor} callback, all other registered but unexecuted
+ * newExecutorService} callback, all other registered but unexecuted
* listeners are prevented from running, even if those listeners are to run
* in other executors.
*
@@ -218,7 +221,7 @@ public static ListenableFuture immediateFailedFuture(
* thread that called {@code transform}. Second, transformations may run in
* an internal thread of the system responsible for the input {@code Future},
* such as an RPC network thread. Finally, during the execution of a {@code
- * sameThreadExecutor} transformation, all other registered but unexecuted
+ * newExecutorService} transformation, all other registered but unexecuted
* listeners are prevented from running, even if those listeners are to run
* in other executors.
*
@@ -240,7 +243,7 @@ public static ListenableFuture immediateFailedFuture(
*/
public static ListenableFuture transform(ListenableFuture future,
final Function super I, ? extends O> function) {
- return Futures.transform(future, function, MoreExecutors.sameThreadExecutor());
+ return Futures.transform(future, function, newExecutorService());
}
/**
@@ -272,7 +275,7 @@ public static ListenableFuture transform(ListenableFuture future,
* Note: For cases in which the transformation is fast and lightweight,
* consider {@linkplain Futures#transform(ListenableFuture, Function) the
* other overload} or explicit use of {@link
- * MoreExecutors#sameThreadExecutor}. For heavier transformations, this
+ * #newExecutorService}. For heavier transformations, this
* choice carries some caveats: First, the thread that the transformation
* runs in depends on whether the input {@code Future} is done at the time
* {@code transform} is called. In particular, if called late, {@code
@@ -280,7 +283,7 @@ public static ListenableFuture transform(ListenableFuture future,
* {@code transform}. Second, transformations may run in an internal thread
* of the system responsible for the input {@code Future}, such as an RPC
* network thread. Finally, during the execution of a {@code
- * sameThreadExecutor} transformation, all other registered but unexecuted
+ * newExecutorService} transformation, all other registered but unexecuted
* listeners are prevented from running, even if those listeners are to run
* in other executors.
*
@@ -304,6 +307,29 @@ public static ListenableFuture transform(ListenableFuture future,
return chain(future, wrapperFunction, executor);
}
+ /**
+ * Returns an {@link ExecutorService} to be used as a parameter in other methods.
+ * It calls {@code MoreExecutors#newDirectExecutorService} or falls back to {@code MoreExecutors#sameThreadExecutor}
+ * for compatibility with older (< 18.0) versions of guava.
+ *
+ * @since TODO
+ */
+ public static ExecutorService newExecutorService() {
+ try {
+ try {
+ // Guava older than 18
+ Method method = MoreExecutors.class.getMethod("sameThreadExecutor");
+ return (ExecutorService) method.invoke(null);
+ } catch (NoSuchMethodException e) {
+ // TODO invert this to prefer the newer guava method once guava is upgrade in Jenkins core
+ Method method = MoreExecutors.class.getMethod("newDirectExecutorService");
+ return (ExecutorService) method.invoke(null);
+ }
+ } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e ) {
+ throw new RuntimeException(e);
+ }
+ }
+
/**
* Creates a new {@code ListenableFuture} whose value is a list containing the
* values of all its input futures, if all succeed. If any input fails, the
@@ -324,7 +350,7 @@ public static ListenableFuture transform(ListenableFuture future,
public static ListenableFuture> allAsList(
ListenableFuture extends V>... futures) {
return new ListFuture(ImmutableList.copyOf(futures), true,
- MoreExecutors.sameThreadExecutor());
+ newExecutorService());
}
/**
@@ -347,7 +373,7 @@ public static ListenableFuture> allAsList(
public static ListenableFuture> allAsList(
Iterable extends ListenableFuture extends V>> futures) {
return new ListFuture(ImmutableList.copyOf(futures), true,
- MoreExecutors.sameThreadExecutor());
+ newExecutorService());
}
/**
@@ -379,14 +405,14 @@ public static ListenableFuture> allAsList(
* Note: For cases in which the work of creating the derived future is
* fast and lightweight, consider {@linkplain Futures#chain(ListenableFuture,
* Function) the other overload} or explicit use of {@code
- * sameThreadExecutor}. For heavier derivations, this choice carries some
+ * newExecutorService}. For heavier derivations, this choice carries some
* caveats: First, the thread that the derivation runs in depends on whether
* the input {@code Future} is done at the time {@code chain} is called. In
* particular, if called late, {@code chain} will run the derivation in the
* thread that called {@code chain}. Second, derivations may run in an
* internal thread of the system responsible for the input {@code Future},
* such as an RPC network thread. Finally, during the execution of a {@code
- * sameThreadExecutor} {@code chain} function, all other registered but
+ * newExecutorService} {@code chain} function, all other registered but
* unexecuted listeners are prevented from running, even if those listeners
* are to run in other executors.
*
diff --git a/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/ListFuture.java b/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/ListFuture.java
index 61322fd1..a7c961be 100644
--- a/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/ListFuture.java
+++ b/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/ListFuture.java
@@ -75,7 +75,7 @@ public void run() {
// Let go of the memory held by other futures
ListFuture.this.futures = null;
}
- }, MoreExecutors.sameThreadExecutor());
+ }, Futures.newExecutorService());
// Now begin the "real" initialization.