From ec98115425afc6cbb1f808914bbf3f3791557c64 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Sun, 14 Mar 2021 07:36:01 -0700 Subject: [PATCH 1/7] Add MoreExecutors#directExecutor() implementation from Guava 30.1 --- .../support/concurrent/DirectExecutor.java | 37 ++++++++ .../support/concurrent/MoreExecutors.java | 85 +++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/DirectExecutor.java create mode 100644 src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/MoreExecutors.java diff --git a/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/DirectExecutor.java b/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/DirectExecutor.java new file mode 100644 index 00000000..cf5067ac --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/DirectExecutor.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package org.jenkinsci.plugins.workflow.support.concurrent; + +import com.google.common.annotations.GwtCompatible; +import java.util.concurrent.Executor; + +/** + * An {@link Executor} that runs each task in the thread that invokes {@link Executor#execute + * execute}. + */ +@GwtCompatible +enum DirectExecutor implements Executor { + INSTANCE; + + @Override + public void execute(Runnable command) { + command.run(); + } + + @Override + public String toString() { + return "MoreExecutors.directExecutor()"; + } +} diff --git a/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/MoreExecutors.java b/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/MoreExecutors.java new file mode 100644 index 00000000..c7887b44 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/MoreExecutors.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package org.jenkinsci.plugins.workflow.support.concurrent; + +import com.google.common.annotations.GwtCompatible; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy; + +/** + * Factory and utility methods for {@link java.util.concurrent.Executor}, {@link ExecutorService}, + * and {@link java.util.concurrent.ThreadFactory}. + * + * @author Eric Fellheimer + * @author Kyle Littlefield + * @author Justin Mahoney + * @since 3.0 + */ +@GwtCompatible(emulated = true) +public final class MoreExecutors { + private MoreExecutors() {} + + /** + * Returns an {@link Executor} that runs each task in the thread that invokes {@link + * Executor#execute execute}, as in {@code ThreadPoolExecutor.CallerRunsPolicy}. + * + *

This executor is appropriate for tasks that are lightweight and not deeply chained. + * Inappropriate {@code directExecutor} usage can cause problems, and these problems can be + * difficult to reproduce because they depend on timing. For example: + * + *

+ * + * Additionally, beware of executing tasks with {@code directExecutor} while holding a lock. Since + * the task you submit to the executor (or any other arbitrary work the executor does) may do slow + * work or acquire other locks, you risk deadlocks. + * + *

This instance is equivalent to: + * + *

{@code
+   * final class DirectExecutor implements Executor {
+   *   public void execute(Runnable r) {
+   *     r.run();
+   *   }
+   * }
+   * }
+ * + *

This should be preferred to {@link #newDirectExecutorService()} because implementing the + * {@link ExecutorService} subinterface necessitates significant performance overhead. + * + * + * @since 18.0 + */ + public static Executor directExecutor() { + return DirectExecutor.INSTANCE; + } +} From 43836aa1970e5daf7ec4273ae1963375baf995d2 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Sun, 14 Mar 2021 07:36:11 -0700 Subject: [PATCH 2/7] Sync with Guava 12 Pull in updates from: MOE_MIGRATED_REVID=27031992 --- .../workflow/support/concurrent/Futures.java | 198 +++++++++++------- 1 file changed, 119 insertions(+), 79 deletions(-) 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 9b313f24..87bdfcd7 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 @@ -194,6 +194,117 @@ public static ListenableFuture immediateFailedFuture( return future; } + /** + * Returns a new {@code ListenableFuture} whose result is asynchronously + * derived from the result of the given {@code Future}. More precisely, the + * returned {@code Future} takes its result from a {@code Future} produced by + * applying the given {@code AsyncFunction} to the result of the original + * {@code Future}. Example: + * + *

   {@code
+     *   ListenableFuture rowKeyFuture = indexService.lookUp(query);
+     *   AsyncFunction queryFunction =
+     *       new AsyncFunction() {
+     *         public ListenableFuture apply(RowKey rowKey) {
+     *           return dataService.read(rowKey);
+     *         }
+     *       };
+     *   ListenableFuture queryFuture =
+     *       transform(rowKeyFuture, queryFunction);
+     * }
+ * + *

Note: This overload of {@code transform} is designed for cases in which + * the work of creating the derived {@code Future} is fast and lightweight, + * as the method does not accept an {@code Executor} in which to perform the + * the work. (The created {@code Future} itself need not complete quickly.) + * For heavier operations, this overload carries some caveats: First, the + * thread that {@code function.apply} runs in depends on whether the input + * {@code Future} is done at the time {@code transform} is called. In + * particular, if called late, {@code transform} will run the operation in + * the thread that called {@code transform}. Second, {@code function.apply} + * 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 function.apply}, all + * other registered but unexecuted listeners are prevented from running, even + * if those listeners are to run in other executors. + * + *

The returned {@code Future} attempts to keep its cancellation state in + * sync with that of the input future and that of the future returned by the + * function. That is, if the returned {@code Future} is cancelled, it will + * attempt to cancel the other two, and if either of the other two is + * cancelled, the returned {@code Future} will receive a callback in which it + * will attempt to cancel itself. + * + * @param input The future to transform + * @param function A function to transform the result of the input future + * to the result of the output future + * @return A future that holds result of the function (if the input succeeded) + * or the original input's failure (if not) + * @since 11.0 + */ + public static ListenableFuture transform(ListenableFuture input, + AsyncFunction function) { + return transform(input, function, MoreExecutors.sameThreadExecutor()); + } + + /** + * Returns a new {@code ListenableFuture} whose result is asynchronously + * derived from the result of the given {@code Future}. More precisely, the + * returned {@code Future} takes its result from a {@code Future} produced by + * applying the given {@code AsyncFunction} to the result of the original + * {@code Future}. Example: + * + *

   {@code
+     *   ListenableFuture rowKeyFuture = indexService.lookUp(query);
+     *   AsyncFunction queryFunction =
+     *       new AsyncFunction() {
+     *         public ListenableFuture apply(RowKey rowKey) {
+     *           return dataService.read(rowKey);
+     *         }
+     *       };
+     *   ListenableFuture queryFuture =
+     *       transform(rowKeyFuture, queryFunction, executor);
+     * }
+ * + *

The returned {@code Future} attempts to keep its cancellation state in + * sync with that of the input future and that of the future returned by the + * chain function. That is, if the returned {@code Future} is cancelled, it + * will attempt to cancel the other two, and if either of the other two is + * cancelled, the returned {@code Future} will receive a callback in which it + * will attempt to cancel itself. + * + *

Note: For cases in which the work of creating the derived future is + * fast and lightweight, consider {@linkplain + * Futures#transform(ListenableFuture, Function) the other overload} or + * explicit use of {@code sameThreadExecutor}. For heavier derivations, this + * choice carries some caveats: First, the thread that {@code function.apply} + * runs in depends on whether the input {@code Future} is done at the time + * {@code transform} is called. In particular, if called late, {@code + * transform} will run the operation in the thread that called {@code + * transform}. Second, {@code function.apply} 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 function.apply}, all other registered but + * unexecuted listeners are prevented from running, even if those listeners + * are to run in other executors. + * + * @param input The future to transform + * @param function A function to transform the result of the input future + * to the result of the output future + * @param executor Executor to run the function in. + * @return A future that holds result of the function (if the input succeeded) + * or the original input's failure (if not) + * @since 11.0 + */ + public static ListenableFuture transform(ListenableFuture input, + AsyncFunction function, + Executor executor) { + ChainingListenableFuture output = + new ChainingListenableFuture(function, input); + input.addListener(output, executor); + return output; + } + /** * Returns a new {@code ListenableFuture} whose result is the product of * applying the given {@code Function} to the result of the given {@code @@ -234,16 +345,16 @@ public static ListenableFuture immediateFailedFuture( *

An example use of this method is to convert a serializable object * returned from an RPC into a POJO. * - * @param future The future to transform + * @param input The future to transform * @param function A Function to transform the results of the provided future * to the results of the returned future. This will be run in the thread * that notifies input it is complete. * @return A future that holds result of the transformation. * @since 9.0 (in 1.0 as {@code compose}) */ - public static ListenableFuture transform(ListenableFuture future, + public static ListenableFuture transform(ListenableFuture input, final Function function) { - return Futures.transform(future, function, newExecutorService()); + return transform(input, function, MoreExecutors.sameThreadExecutor()); } /** @@ -287,24 +398,24 @@ public static ListenableFuture transform(ListenableFuture future, * listeners are prevented from running, even if those listeners are to run * in other executors. * - * @param future The future to transform + * @param input The future to transform * @param function A Function to transform the results of the provided future * to the results of the returned future. * @param executor Executor to run the function in. * @return A future that holds result of the transformation. * @since 9.0 (in 2.0 as {@code compose}) */ - public static ListenableFuture transform(ListenableFuture future, + public static ListenableFuture transform(ListenableFuture input, final Function function, Executor executor) { checkNotNull(function); - Function> wrapperFunction - = new Function>() { + AsyncFunction wrapperFunction + = new AsyncFunction() { @Override public ListenableFuture apply(I input) { O output = function.apply(input); return immediateFuture(output); } }; - return chain(future, wrapperFunction, executor); + return transform(input, wrapperFunction, executor); } /** @@ -375,75 +486,4 @@ public static ListenableFuture> allAsList( return new ListFuture(ImmutableList.copyOf(futures), true, newExecutorService()); } - - /** - *

Returns a new {@code ListenableFuture} whose result is asynchronously - * derived from the result of the given {@code Future}. More precisely, the - * returned {@code Future} takes its result from a {@code Future} produced by - * applying the given {@code Function} to the result of the original {@code - * Future}. Example: - * - *

   {@code
-     *   ListenableFuture rowKeyFuture = indexService.lookUp(query);
-     *   Function> queryFunction =
-     *       new Function>() {
-     *         public ListenableFuture apply(RowKey rowKey) {
-     *           return dataService.read(rowKey);
-     *         }
-     *       };
-     *   ListenableFuture queryFuture =
-     *       chain(rowKeyFuture, queryFunction, executor);
-     * }
- * - *

The returned {@code Future} attempts to keep its cancellation state in - * sync with that of the input future and that of the future returned by the - * chain function. That is, if the returned {@code Future} is cancelled, it - * will attempt to cancel the other two, and if either of the other two is - * cancelled, the returned {@code Future} will receive a callback in which it - * will attempt to cancel itself. - * - *

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 - * 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 - * newExecutorService} {@code chain} function, all other registered but - * unexecuted listeners are prevented from running, even if those listeners - * are to run in other executors. - * - * @param input The future to chain - * @param function A function to chain the results of the provided future - * to the results of the returned future. - * @param executor Executor to run the function in. - * @return A future that holds result of the chain. - * @deprecated Convert your {@code Function} to a {@code AsyncFunction}, and - * use {@link #transform(ListenableFuture, AsyncFunction, Executor)}. This - * method is scheduled to be removed from Guava in Guava release 12.0. - */ - @Deprecated - /*package*/ static ListenableFuture chain(ListenableFuture input, - final Function> - function, - Executor executor) { - checkNotNull(function); - ChainingListenableFuture chain = - new ChainingListenableFuture(new AsyncFunction() { - @Override - /* - * All methods of ListenableFuture are covariant, and we don't expose - * the object anywhere that would allow it to be downcast. - */ - @SuppressWarnings("unchecked") - public ListenableFuture apply(I input) { - return (ListenableFuture) function.apply(input); - } - }, input); - input.addListener(chain, executor); - return chain; - } } From 372659ee36ca9c365ea3ed2b5479b3d3b66a1a65 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Sun, 14 Mar 2021 07:36:15 -0700 Subject: [PATCH 3/7] Sync with Guava 13 Pull in updates from: MOE_MIGRATED_REVID=31833262 --- .../workflow/support/concurrent/Futures.java | 157 +++++++++--------- 1 file changed, 75 insertions(+), 82 deletions(-) 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 87bdfcd7..145e24ca 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 @@ -67,19 +67,26 @@ public abstract class Futures { * } * });} * - *

Note: This overload of {@code addCallback} is designed for cases in - * 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 - * time {@code addCallback} is called and on whether the input {@code Future} - * is ever cancelled. In particular, {@code addCallback} may execute the - * 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 newExecutorService} - * callback, all other registered but unexecuted listeners are prevented from - * running, even if those listeners are to run in other executors. + * Note: If the callback is slow or heavyweight, consider {@linkplain + * #addCallback(ListenableFuture, FutureCallback, Executor) supplying an + * executor}. If you do not supply an executor, {@code addCallback} will use + * {@link MoreExecutors#sameThreadExecutor sameThreadExecutor}, which carries + * some caveats for heavier operations. For example, the callback may run on + * an unpredictable or undesirable thread: + * + *

    + *
  • If the input {@code Future} is done at the time {@code addCallback} is + * called, {@code addCallback} will execute the callback inline. + *
  • If the input {@code Future} is not yet done, {@code addCallback} will + * schedule the callback to be run by the thread that completes the input + * {@code Future}, which may be an internal system thread such as an RPC + * network thread. + *
+ * + * Also note that, regardless of which thread executes the callback, all + * other registered but unexecuted listeners are prevented from running + * during its execution, even if those listeners are to run in other + * executors. * *

For a more general interface to attach a completion listener to a * {@code Future}, see {@link ListenableFuture#addListener addListener}. @@ -116,20 +123,10 @@ 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 #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, - * {@code addCallback} may execute the 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 - * newExecutorService} callback, all other registered but unexecuted - * listeners are prevented from running, even if those listeners are to run - * in other executors. + * When the callback is fast and lightweight, consider {@linkplain + * #addCallback(ListenableFuture, FutureCallback) omitting the executor} or + * explicitly specifying {@code sameThreadExecutor}. However, be aware of the + * caveats documented in the link above. * *

For a more general interface to attach a completion listener to a * {@code Future}, see {@link ListenableFuture#addListener addListener}. @@ -213,20 +210,28 @@ public static ListenableFuture immediateFailedFuture( * transform(rowKeyFuture, queryFunction); * } * - *

Note: This overload of {@code transform} is designed for cases in which - * the work of creating the derived {@code Future} is fast and lightweight, - * as the method does not accept an {@code Executor} in which to perform the - * the work. (The created {@code Future} itself need not complete quickly.) - * For heavier operations, this overload carries some caveats: First, the - * thread that {@code function.apply} runs in depends on whether the input - * {@code Future} is done at the time {@code transform} is called. In - * particular, if called late, {@code transform} will run the operation in - * the thread that called {@code transform}. Second, {@code function.apply} - * 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 function.apply}, all - * other registered but unexecuted listeners are prevented from running, even - * if those listeners are to run in other executors. + * Note: If the derived {@code Future} is slow or heavyweight to create + * (whether the {@code Future} itself is slow or heavyweight to complete is + * irrelevant), consider {@linkplain #transform(ListenableFuture, + * AsyncFunction, Executor) supplying an executor}. If you do not supply an + * executor, {@code transform} will use {@link + * MoreExecutors#sameThreadExecutor sameThreadExecutor}, which carries some + * caveats for heavier operations. For example, the call to {@code + * function.apply} may run on an unpredictable or undesirable thread: + * + *

    + *
  • If the input {@code Future} is done at the time {@code transform} is + * called, {@code transform} will call {@code function.apply} inline. + *
  • If the input {@code Future} is not yet done, {@code transform} will + * schedule {@code function.apply} to be run by the thread that completes the + * input {@code Future}, which may be an internal system thread such as an + * RPC network thread. + *
+ * + * Also note that, regardless of which thread executes {@code + * function.apply}, all other registered but unexecuted listeners are + * prevented from running during its execution, even if those listeners are + * to run in other executors. * *

The returned {@code Future} attempts to keep its cancellation state in * sync with that of the input future and that of the future returned by the @@ -273,20 +278,11 @@ public static ListenableFuture transform(ListenableFuture input, * cancelled, the returned {@code Future} will receive a callback in which it * will attempt to cancel itself. * - *

Note: For cases in which the work of creating the derived future is - * fast and lightweight, consider {@linkplain - * Futures#transform(ListenableFuture, Function) the other overload} or - * explicit use of {@code sameThreadExecutor}. For heavier derivations, this - * choice carries some caveats: First, the thread that {@code function.apply} - * runs in depends on whether the input {@code Future} is done at the time - * {@code transform} is called. In particular, if called late, {@code - * transform} will run the operation in the thread that called {@code - * transform}. Second, {@code function.apply} 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 function.apply}, all other registered but - * unexecuted listeners are prevented from running, even if those listeners - * are to run in other executors. + *

When the execution of {@code function.apply} is fast and lightweight + * (though the {@code Future} it returns need not meet these criteria), + * consider {@linkplain #transform(ListenableFuture, AsyncFunction) omitting + * the executor} or explicitly specifying {@code sameThreadExecutor}. + * However, be aware of the caveats documented in the link above. * * @param input The future to transform * @param function A function to transform the result of the input future @@ -322,19 +318,26 @@ public static ListenableFuture transform(ListenableFuture input, * transform(queryFuture, rowsFunction); * } * - *

Note: This overload of {@code transform} is designed for cases in which - * the transformation is fast and lightweight, as the method does not accept - * an {@code Executor} in which to perform the the work. For heavier - * transformations, this overload 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 transform} will perform the transformation in the - * 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 - * newExecutorService} transformation, all other registered but unexecuted - * listeners are prevented from running, even if those listeners are to run - * in other executors. + * Note: If the transformation is slow or heavyweight, consider {@linkplain + * #transform(ListenableFuture, Function, Executor) supplying an executor}. + * If you do not supply an executor, {@code transform} will use {@link + * MoreExecutors#sameThreadExecutor sameThreadExecutor}, which carries some + * caveats for heavier operations. For example, the call to {@code + * function.apply} may run on an unpredictable or undesirable thread: + * + *

    + *
  • If the input {@code Future} is done at the time {@code transform} is + * called, {@code transform} will call {@code function.apply} inline. + *
  • If the input {@code Future} is not yet done, {@code transform} will + * schedule {@code function.apply} to be run by the thread that completes the + * input {@code Future}, which may be an internal system thread such as an + * RPC network thread. + *
+ * + * Also note that, regardless of which thread executes {@code + * function.apply}, all other registered but unexecuted listeners are + * prevented from running during its execution, even if those listeners are + * to run in other executors. * *

The returned {@code Future} attempts to keep its cancellation state in * sync with that of the input future. That is, if the returned {@code Future} @@ -383,20 +386,10 @@ public static ListenableFuture transform(ListenableFuture input, *

An example use of this method is to convert a serializable object * returned from an RPC into a POJO. * - *

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 - * #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 - * transform} will perform the transformation in the 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 - * newExecutorService} transformation, all other registered but unexecuted - * listeners are prevented from running, even if those listeners are to run - * in other executors. + *

When the transformation is fast and lightweight, consider {@linkplain + * #transform(ListenableFuture, Function) omitting the executor} or + * explicitly specifying {@code sameThreadExecutor}. However, be aware of the + * caveats documented in the link above. * * @param input The future to transform * @param function A Function to transform the results of the provided future From 9e70d3758a30fce1b60845dc46d0622f92230e80 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Sun, 14 Mar 2021 07:36:18 -0700 Subject: [PATCH 4/7] Cherry-pick Guava 18 MoreExecutor fixes Pull in updates from: MOE_MIGRATED_REVID=71753284 MOE_MIGRATED_REVID=72669239 --- .../concurrent/ChainingListenableFuture.java | 3 +- .../workflow/support/concurrent/Futures.java | 33 +++++++++---------- .../support/concurrent/ListFuture.java | 3 +- 3 files changed, 18 insertions(+), 21 deletions(-) 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 932a8c14..ff3662bb 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 @@ -3,7 +3,6 @@ import com.google.common.util.concurrent.AbstractFuture; import com.google.common.util.concurrent.AsyncFunction; import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; import javax.annotation.Nullable; import java.lang.reflect.UndeclaredThrowableException; @@ -208,7 +207,7 @@ public void run() { ChainingListenableFuture.this.outputFuture = null; } } - }, Futures.newExecutorService()); + }, MoreExecutors.directExecutor()); } 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 145e24ca..98ca13d3 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 @@ -22,7 +22,6 @@ import com.google.common.util.concurrent.AsyncFunction; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.SettableFuture; import javax.annotation.Nullable; @@ -70,7 +69,7 @@ public abstract class Futures { * Note: If the callback is slow or heavyweight, consider {@linkplain * #addCallback(ListenableFuture, FutureCallback, Executor) supplying an * executor}. If you do not supply an executor, {@code addCallback} will use - * {@link MoreExecutors#sameThreadExecutor sameThreadExecutor}, which carries + * a {@linkplain MoreExecutors#directExecutor direct executor}, which carries * some caveats for heavier operations. For example, the callback may run on * an unpredictable or undesirable thread: * @@ -97,7 +96,7 @@ public abstract class Futures { */ public static void addCallback(ListenableFuture future, FutureCallback callback) { - addCallback(future, callback, newExecutorService()); + addCallback(future, callback, MoreExecutors.directExecutor()); } /** @@ -125,7 +124,7 @@ public static void addCallback(ListenableFuture future, * * When the callback is fast and lightweight, consider {@linkplain * #addCallback(ListenableFuture, FutureCallback) omitting the executor} or - * explicitly specifying {@code sameThreadExecutor}. However, be aware of the + * explicitly specifying {@code directExecutor}. However, be aware of the * caveats documented in the link above. * *

For a more general interface to attach a completion listener to a @@ -214,9 +213,9 @@ public static ListenableFuture immediateFailedFuture( * (whether the {@code Future} itself is slow or heavyweight to complete is * irrelevant), consider {@linkplain #transform(ListenableFuture, * AsyncFunction, Executor) supplying an executor}. If you do not supply an - * executor, {@code transform} will use {@link - * MoreExecutors#sameThreadExecutor sameThreadExecutor}, which carries some - * caveats for heavier operations. For example, the call to {@code + * executor, {@code transform} will use a + * {@linkplain MoreExecutors#directExecutor direct executor}, which carries + * some caveats for heavier operations. For example, the call to {@code * function.apply} may run on an unpredictable or undesirable thread: * *

    @@ -249,7 +248,7 @@ public static ListenableFuture immediateFailedFuture( */ public static ListenableFuture transform(ListenableFuture input, AsyncFunction function) { - return transform(input, function, MoreExecutors.sameThreadExecutor()); + return transform(input, function, MoreExecutors.directExecutor()); } /** @@ -281,7 +280,7 @@ public static ListenableFuture transform(ListenableFuture input, *

    When the execution of {@code function.apply} is fast and lightweight * (though the {@code Future} it returns need not meet these criteria), * consider {@linkplain #transform(ListenableFuture, AsyncFunction) omitting - * the executor} or explicitly specifying {@code sameThreadExecutor}. + * the executor} or explicitly specifying {@code directExecutor}. * However, be aware of the caveats documented in the link above. * * @param input The future to transform @@ -320,10 +319,10 @@ public static ListenableFuture transform(ListenableFuture input, * * Note: If the transformation is slow or heavyweight, consider {@linkplain * #transform(ListenableFuture, Function, Executor) supplying an executor}. - * If you do not supply an executor, {@code transform} will use {@link - * MoreExecutors#sameThreadExecutor sameThreadExecutor}, which carries some - * caveats for heavier operations. For example, the call to {@code - * function.apply} may run on an unpredictable or undesirable thread: + * If you do not supply an executor, {@code transform} will use an inline + * executor, which carries some caveats for heavier operations. For example, + * the call to {@code function.apply} may run on an unpredictable or + * undesirable thread: * *

      *
    • If the input {@code Future} is done at the time {@code transform} is @@ -357,7 +356,7 @@ public static ListenableFuture transform(ListenableFuture input, */ public static ListenableFuture transform(ListenableFuture input, final Function function) { - return transform(input, function, MoreExecutors.sameThreadExecutor()); + return transform(input, function, MoreExecutors.directExecutor()); } /** @@ -388,7 +387,7 @@ public static ListenableFuture transform(ListenableFuture input, * *

      When the transformation is fast and lightweight, consider {@linkplain * #transform(ListenableFuture, Function) omitting the executor} or - * explicitly specifying {@code sameThreadExecutor}. However, be aware of the + * explicitly specifying {@code directExecutor}. However, be aware of the * caveats documented in the link above. * * @param input The future to transform @@ -454,7 +453,7 @@ public static ExecutorService newExecutorService() { public static ListenableFuture> allAsList( ListenableFuture... futures) { return new ListFuture(ImmutableList.copyOf(futures), true, - newExecutorService()); + MoreExecutors.directExecutor()); } /** @@ -477,6 +476,6 @@ public static ListenableFuture> allAsList( public static ListenableFuture> allAsList( Iterable> futures) { return new ListFuture(ImmutableList.copyOf(futures), true, - newExecutorService()); + MoreExecutors.directExecutor()); } } 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 a7c961be..4293ad06 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 @@ -19,7 +19,6 @@ import com.google.common.collect.Lists; import com.google.common.util.concurrent.AbstractFuture; import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; import java.util.List; import java.util.concurrent.CancellationException; @@ -75,7 +74,7 @@ public void run() { // Let go of the memory held by other futures ListFuture.this.futures = null; } - }, Futures.newExecutorService()); + }, MoreExecutors.directExecutor()); // Now begin the "real" initialization. From 65ea927db3dc21e58d80156d712091bcd97c49b4 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Sun, 14 Mar 2021 07:36:21 -0700 Subject: [PATCH 5/7] Eliminate use of reflection --- .../workflow/support/concurrent/Futures.java | 26 ------------------- 1 file changed, 26 deletions(-) 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 98ca13d3..38172c37 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 @@ -25,12 +25,9 @@ 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; @@ -410,29 +407,6 @@ public static ListenableFuture transform(ListenableFuture input, return transform(input, 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 From d59cebc96251ffe3f7cab74c02f55f0937b5a697 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Sun, 14 Mar 2021 08:15:08 -0700 Subject: [PATCH 6/7] Decrease visibility --- .../workflow/support/concurrent/ChainingListenableFuture.java | 1 + .../plugins/workflow/support/concurrent/DirectExecutor.java | 3 +++ .../jenkinsci/plugins/workflow/support/concurrent/Futures.java | 1 + .../plugins/workflow/support/concurrent/ListFuture.java | 1 + .../plugins/workflow/support/concurrent/MoreExecutors.java | 3 +++ 5 files changed, 9 insertions(+) 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 ff3662bb..8e3e3b3d 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 @@ -29,6 +29,7 @@ * in a {@code UndeclaredThrowableException} so that this class can get * access to the cause. */ +@Deprecated class ChainingListenableFuture extends AbstractFuture implements Runnable { diff --git a/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/DirectExecutor.java b/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/DirectExecutor.java index cf5067ac..c9ec3cf1 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/DirectExecutor.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/DirectExecutor.java @@ -15,6 +15,8 @@ package org.jenkinsci.plugins.workflow.support.concurrent; import com.google.common.annotations.GwtCompatible; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; import java.util.concurrent.Executor; /** @@ -22,6 +24,7 @@ * execute}. */ @GwtCompatible +@Restricted(NoExternalUse.class) enum DirectExecutor implements Executor { INSTANCE; 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 38172c37..d0a44f4f 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 @@ -41,6 +41,7 @@ * * @author Guava */ +@Deprecated public abstract class Futures { /** * Registers separate success and failure callbacks to be run when the {@code 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 4293ad06..0b5cb215 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 @@ -36,6 +36,7 @@ * each component future to fill out the value in the List when that future * completes. */ +@Deprecated class ListFuture extends AbstractFuture> { ImmutableList> futures; final boolean allMustSucceed; diff --git a/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/MoreExecutors.java b/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/MoreExecutors.java index c7887b44..2438ff7b 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/MoreExecutors.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/MoreExecutors.java @@ -15,6 +15,8 @@ package org.jenkinsci.plugins.workflow.support.concurrent; import com.google.common.annotations.GwtCompatible; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadFactory; @@ -30,6 +32,7 @@ * @since 3.0 */ @GwtCompatible(emulated = true) +@Restricted(NoExternalUse.class) public final class MoreExecutors { private MoreExecutors() {} From e295dc5811b43b4099965709b9811a094ddac315 Mon Sep 17 00:00:00 2001 From: Basil Crow Date: Sun, 14 Mar 2021 08:53:12 -0700 Subject: [PATCH 7/7] Fix Javadoc errors --- .../plugins/workflow/support/concurrent/MoreExecutors.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/MoreExecutors.java b/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/MoreExecutors.java index 2438ff7b..ce57ae80 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/MoreExecutors.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/support/concurrent/MoreExecutors.java @@ -57,7 +57,7 @@ private MoreExecutors() {} * tasks -- even tasks that are not themselves {@code directExecutor} tasks. *

    • If many such tasks are chained together (such as with {@code * future.transform(...).transform(...).transform(...)....}), they may overflow the stack. - * (In simple cases, callers can avoid this by registering all tasks with the same {@link + * (In simple cases, callers can avoid this by registering all tasks with the same {@code * MoreExecutors#newSequentialExecutor} wrapper around {@code directExecutor()}. More * complex cases may require using thread pools or making deeper changes.) *
    @@ -76,7 +76,7 @@ private MoreExecutors() {} * } * } * - *

    This should be preferred to {@link #newDirectExecutorService()} because implementing the + *

    This should be preferred to {@code #newDirectExecutorService()} because implementing the * {@link ExecutorService} subinterface necessitates significant performance overhead. * *