From 7c934a2d5823ab8c675af1140bf686557948dcb5 Mon Sep 17 00:00:00 2001 From: Stefan Haustein Date: Wed, 15 May 2024 03:37:26 -0700 Subject: [PATCH] Add a J2kt super-source implementation of DirectExecutorService that doesn't rely on other unsupported classes. RELNOTES=n/a PiperOrigin-RevId: 633880474 --- .../concurrent/DirectExecutorService.java | 129 ++++++++++++++++++ .../common/util/concurrent/MoreExecutors.java | 107 --------------- .../concurrent/DirectExecutorService.java | 129 ++++++++++++++++++ .../common/util/concurrent/MoreExecutors.java | 107 --------------- 4 files changed, 258 insertions(+), 214 deletions(-) create mode 100644 android/guava/src/com/google/common/util/concurrent/DirectExecutorService.java create mode 100644 guava/src/com/google/common/util/concurrent/DirectExecutorService.java diff --git a/android/guava/src/com/google/common/util/concurrent/DirectExecutorService.java b/android/guava/src/com/google/common/util/concurrent/DirectExecutorService.java new file mode 100644 index 000000000000..b39ac6785147 --- /dev/null +++ b/android/guava/src/com/google/common/util/concurrent/DirectExecutorService.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2024 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 com.google.common.util.concurrent; + +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.errorprone.annotations.concurrent.GuardedBy; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; + +/** See newDirectExecutorService javadoc for behavioral notes. */ +@J2ktIncompatible // Emulated +@GwtIncompatible +@ElementTypesAreNonnullByDefault +final class DirectExecutorService extends AbstractListeningExecutorService { + + /** Lock used whenever accessing the state variables (runningTasks, shutdown) of the executor */ + private final Object lock = new Object(); + + /* + * Conceptually, these two variables describe the executor being in + * one of three states: + * - Active: shutdown == false + * - Shutdown: runningTasks > 0 and shutdown == true + * - Terminated: runningTasks == 0 and shutdown == true + */ + @GuardedBy("lock") + private int runningTasks = 0; + + @GuardedBy("lock") + private boolean shutdown = false; + + @Override + public void execute(Runnable command) { + startTask(); + try { + command.run(); + } finally { + endTask(); + } + } + + @Override + public boolean isShutdown() { + synchronized (lock) { + return shutdown; + } + } + + @Override + public void shutdown() { + synchronized (lock) { + shutdown = true; + if (runningTasks == 0) { + lock.notifyAll(); + } + } + } + + // See newDirectExecutorService javadoc for unusual behavior of this method. + @Override + public List shutdownNow() { + shutdown(); + return Collections.emptyList(); + } + + @Override + public boolean isTerminated() { + synchronized (lock) { + return shutdown && runningTasks == 0; + } + } + + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + long nanos = unit.toNanos(timeout); + synchronized (lock) { + while (true) { + if (shutdown && runningTasks == 0) { + return true; + } else if (nanos <= 0) { + return false; + } else { + long now = System.nanoTime(); + TimeUnit.NANOSECONDS.timedWait(lock, nanos); + nanos -= System.nanoTime() - now; // subtract the actual time we waited + } + } + } + } + + /** + * Checks if the executor has been shut down and increments the running task count. + * + * @throws RejectedExecutionException if the executor has been previously shutdown + */ + private void startTask() { + synchronized (lock) { + if (shutdown) { + throw new RejectedExecutionException("Executor already shutdown"); + } + runningTasks++; + } + } + + /** Decrements the running task count. */ + private void endTask() { + synchronized (lock) { + int numRunning = --runningTasks; + if (numRunning == 0) { + lock.notifyAll(); + } + } + } +} diff --git a/android/guava/src/com/google/common/util/concurrent/MoreExecutors.java b/android/guava/src/com/google/common/util/concurrent/MoreExecutors.java index eb3ceed19d8c..064537badcde 100644 --- a/android/guava/src/com/google/common/util/concurrent/MoreExecutors.java +++ b/android/guava/src/com/google/common/util/concurrent/MoreExecutors.java @@ -28,10 +28,8 @@ import com.google.common.collect.Queues; import com.google.common.util.concurrent.ForwardingListenableFuture.SimpleForwardingListenableFuture; import com.google.errorprone.annotations.CanIgnoreReturnValue; -import com.google.errorprone.annotations.concurrent.GuardedBy; import java.lang.reflect.InvocationTargetException; import java.util.Collection; -import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.concurrent.BlockingQueue; @@ -239,110 +237,6 @@ private static void useDaemonThreadFactory(ThreadPoolExecutor executor) { .build()); } - // See newDirectExecutorService javadoc for behavioral notes. - @J2ktIncompatible - @GwtIncompatible // TODO - private static final class DirectExecutorService extends AbstractListeningExecutorService { - /** Lock used whenever accessing the state variables (runningTasks, shutdown) of the executor */ - private final Object lock = new Object(); - - /* - * Conceptually, these two variables describe the executor being in - * one of three states: - * - Active: shutdown == false - * - Shutdown: runningTasks > 0 and shutdown == true - * - Terminated: runningTasks == 0 and shutdown == true - */ - @GuardedBy("lock") - private int runningTasks = 0; - - @GuardedBy("lock") - private boolean shutdown = false; - - @Override - public void execute(Runnable command) { - startTask(); - try { - command.run(); - } finally { - endTask(); - } - } - - @Override - public boolean isShutdown() { - synchronized (lock) { - return shutdown; - } - } - - @Override - public void shutdown() { - synchronized (lock) { - shutdown = true; - if (runningTasks == 0) { - lock.notifyAll(); - } - } - } - - // See newDirectExecutorService javadoc for unusual behavior of this method. - @Override - public List shutdownNow() { - shutdown(); - return Collections.emptyList(); - } - - @Override - public boolean isTerminated() { - synchronized (lock) { - return shutdown && runningTasks == 0; - } - } - - @Override - public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { - long nanos = unit.toNanos(timeout); - synchronized (lock) { - while (true) { - if (shutdown && runningTasks == 0) { - return true; - } else if (nanos <= 0) { - return false; - } else { - long now = System.nanoTime(); - TimeUnit.NANOSECONDS.timedWait(lock, nanos); - nanos -= System.nanoTime() - now; // subtract the actual time we waited - } - } - } - } - - /** - * Checks if the executor has been shut down and increments the running task count. - * - * @throws RejectedExecutionException if the executor has been previously shutdown - */ - private void startTask() { - synchronized (lock) { - if (shutdown) { - throw new RejectedExecutionException("Executor already shutdown"); - } - runningTasks++; - } - } - - /** Decrements the running task count. */ - private void endTask() { - synchronized (lock) { - int numRunning = --runningTasks; - if (numRunning == 0) { - lock.notifyAll(); - } - } - } - } - /** * Creates an executor service that runs each task in the thread that invokes {@code * execute/submit}, as in {@code ThreadPoolExecutor.CallerRunsPolicy}. This applies both to @@ -369,7 +263,6 @@ private void endTask() { * * @since 18.0 (present as MoreExecutors.sameThreadExecutor() since 10.0) */ - @J2ktIncompatible @GwtIncompatible // TODO public static ListeningExecutorService newDirectExecutorService() { return new DirectExecutorService(); diff --git a/guava/src/com/google/common/util/concurrent/DirectExecutorService.java b/guava/src/com/google/common/util/concurrent/DirectExecutorService.java new file mode 100644 index 000000000000..b39ac6785147 --- /dev/null +++ b/guava/src/com/google/common/util/concurrent/DirectExecutorService.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2024 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 com.google.common.util.concurrent; + +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.J2ktIncompatible; +import com.google.errorprone.annotations.concurrent.GuardedBy; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; + +/** See newDirectExecutorService javadoc for behavioral notes. */ +@J2ktIncompatible // Emulated +@GwtIncompatible +@ElementTypesAreNonnullByDefault +final class DirectExecutorService extends AbstractListeningExecutorService { + + /** Lock used whenever accessing the state variables (runningTasks, shutdown) of the executor */ + private final Object lock = new Object(); + + /* + * Conceptually, these two variables describe the executor being in + * one of three states: + * - Active: shutdown == false + * - Shutdown: runningTasks > 0 and shutdown == true + * - Terminated: runningTasks == 0 and shutdown == true + */ + @GuardedBy("lock") + private int runningTasks = 0; + + @GuardedBy("lock") + private boolean shutdown = false; + + @Override + public void execute(Runnable command) { + startTask(); + try { + command.run(); + } finally { + endTask(); + } + } + + @Override + public boolean isShutdown() { + synchronized (lock) { + return shutdown; + } + } + + @Override + public void shutdown() { + synchronized (lock) { + shutdown = true; + if (runningTasks == 0) { + lock.notifyAll(); + } + } + } + + // See newDirectExecutorService javadoc for unusual behavior of this method. + @Override + public List shutdownNow() { + shutdown(); + return Collections.emptyList(); + } + + @Override + public boolean isTerminated() { + synchronized (lock) { + return shutdown && runningTasks == 0; + } + } + + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + long nanos = unit.toNanos(timeout); + synchronized (lock) { + while (true) { + if (shutdown && runningTasks == 0) { + return true; + } else if (nanos <= 0) { + return false; + } else { + long now = System.nanoTime(); + TimeUnit.NANOSECONDS.timedWait(lock, nanos); + nanos -= System.nanoTime() - now; // subtract the actual time we waited + } + } + } + } + + /** + * Checks if the executor has been shut down and increments the running task count. + * + * @throws RejectedExecutionException if the executor has been previously shutdown + */ + private void startTask() { + synchronized (lock) { + if (shutdown) { + throw new RejectedExecutionException("Executor already shutdown"); + } + runningTasks++; + } + } + + /** Decrements the running task count. */ + private void endTask() { + synchronized (lock) { + int numRunning = --runningTasks; + if (numRunning == 0) { + lock.notifyAll(); + } + } + } +} diff --git a/guava/src/com/google/common/util/concurrent/MoreExecutors.java b/guava/src/com/google/common/util/concurrent/MoreExecutors.java index a09ea7ead403..903dfa07742f 100644 --- a/guava/src/com/google/common/util/concurrent/MoreExecutors.java +++ b/guava/src/com/google/common/util/concurrent/MoreExecutors.java @@ -29,11 +29,9 @@ import com.google.common.collect.Queues; import com.google.common.util.concurrent.ForwardingListenableFuture.SimpleForwardingListenableFuture; import com.google.errorprone.annotations.CanIgnoreReturnValue; -import com.google.errorprone.annotations.concurrent.GuardedBy; import java.lang.reflect.InvocationTargetException; import java.time.Duration; import java.util.Collection; -import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.concurrent.BlockingQueue; @@ -300,110 +298,6 @@ private static void useDaemonThreadFactory(ThreadPoolExecutor executor) { .build()); } - // See newDirectExecutorService javadoc for behavioral notes. - @J2ktIncompatible - @GwtIncompatible // TODO - private static final class DirectExecutorService extends AbstractListeningExecutorService { - /** Lock used whenever accessing the state variables (runningTasks, shutdown) of the executor */ - private final Object lock = new Object(); - - /* - * Conceptually, these two variables describe the executor being in - * one of three states: - * - Active: shutdown == false - * - Shutdown: runningTasks > 0 and shutdown == true - * - Terminated: runningTasks == 0 and shutdown == true - */ - @GuardedBy("lock") - private int runningTasks = 0; - - @GuardedBy("lock") - private boolean shutdown = false; - - @Override - public void execute(Runnable command) { - startTask(); - try { - command.run(); - } finally { - endTask(); - } - } - - @Override - public boolean isShutdown() { - synchronized (lock) { - return shutdown; - } - } - - @Override - public void shutdown() { - synchronized (lock) { - shutdown = true; - if (runningTasks == 0) { - lock.notifyAll(); - } - } - } - - // See newDirectExecutorService javadoc for unusual behavior of this method. - @Override - public List shutdownNow() { - shutdown(); - return Collections.emptyList(); - } - - @Override - public boolean isTerminated() { - synchronized (lock) { - return shutdown && runningTasks == 0; - } - } - - @Override - public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { - long nanos = unit.toNanos(timeout); - synchronized (lock) { - while (true) { - if (shutdown && runningTasks == 0) { - return true; - } else if (nanos <= 0) { - return false; - } else { - long now = System.nanoTime(); - TimeUnit.NANOSECONDS.timedWait(lock, nanos); - nanos -= System.nanoTime() - now; // subtract the actual time we waited - } - } - } - } - - /** - * Checks if the executor has been shut down and increments the running task count. - * - * @throws RejectedExecutionException if the executor has been previously shutdown - */ - private void startTask() { - synchronized (lock) { - if (shutdown) { - throw new RejectedExecutionException("Executor already shutdown"); - } - runningTasks++; - } - } - - /** Decrements the running task count. */ - private void endTask() { - synchronized (lock) { - int numRunning = --runningTasks; - if (numRunning == 0) { - lock.notifyAll(); - } - } - } - } - /** * Creates an executor service that runs each task in the thread that invokes {@code * execute/submit}, as in {@code ThreadPoolExecutor.CallerRunsPolicy}. This applies both to @@ -430,7 +324,6 @@ private void endTask() { * * @since 18.0 (present as MoreExecutors.sameThreadExecutor() since 10.0) */ - @J2ktIncompatible @GwtIncompatible // TODO public static ListeningExecutorService newDirectExecutorService() { return new DirectExecutorService();